java_security_calendar_2019(day13-day16)

Day13

示例代码:

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
public class UploadFileController extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);

String uploadPath = getServletContext().getRealPath("") + "upload";
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
try {
List<FileItem> items = upload.parseRequest(request);
if (items != null && items.size() > 0) {
for (FileItem item : items) {
if (!item.isFormField()) {
if (!(item.getContentType().equals("text/plain"))) {
throw new Exception("ContentType mismatch");
}
String file = uploadPath + File.separator + item.getName();
File storeFile = new File(file);
item.write(storeFile);
}
}
}
} catch (Exception ex) {
response.sendRedirect("/");
}
}
}

初步看到代码是上传漏洞,只对ContentType的值做了校验,轻松绕过。可是上传文件后,发现并没有返回路径。

然后看到"String file = uploadPath + File.separator + item.getName();",此处的文件名用户可控,且未做任何过滤,可通过"../"来控制上传路径,从而获取webshell。

上传webshell,post包部份数据如下:

1
2
ntent-Disposition: form-data; name="filename"; filename="../shell.jsp"
Content-Type: text/plain

成功收到shell。

Day14

示例代码:

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
import java.io.PrintWriter;
import java.util.*;
import javax.servlet.http.*;

public class Export extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
response.setContentType("text/csv");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();

String content = buildCSV(request);
out.print(content);
out.flush();
}

public String buildCSV(HttpServletRequest request) {
{
StringBuilder str = new StringBuilder();

List<List<String>> rows = Arrays.asList(
Arrays.asList("Scott", "editor", request.getParameter("description"))
);

str.append("Name");
str.append(",");
str.append("Role");
str.append(",");
str.append("Description");
str.append("\n");

for (List<String> rowData : rows) {
str.append(String.join(",", rowData));
str.append("\n");
}

return str.toString();
}
}
}

该示例代码为dde漏洞,响应的数据格式为"text/csv"格式,在传入的数据中,参数"description"可控,可构造以"=""-"打头的恶意语句,如:"description==cmd |'/C calc' !A0"
执行结果:

Day15

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FindOnSystem extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
String[] binary = {"find", ".", "-type", "d"};
ArrayList<String> cmd = new ArrayList<>(Arrays.asList(binary));

String[] options = request.getParameter("options").split(" ");
for (String i : options) {
cmd.add(i);
}

ProcessBuilder processBuilder = new ProcessBuilder(cmd);
Process process = processBuilder.start();
IOUtils.copy(process.getInputStream(),response.getOutputStream());
} catch(Exception e) {
response.sendRedirect("/");
}
}
}

看到敏感的方法"processBuilder.start()",典型的命令执行漏洞。可发现"options"传入的值全部拼接在find命令后,尝试"; pwd""||""&&"等常见命令,发现都不能返回正常结果,(但是在终端可以正常执行,不是很明白为啥)。看了下官方提示,使用的是"options=-exec pwd ;" ,可正常执行。

注find -exec用法:
"find ./ -type f -exec ls -l {} \;" 。 以; 为结束标志,由于各个系统中分号会有不同的意义,因此在前面加上反斜杠。{} 代表前面find查找出来的文件名

执行结果:

Day16

示例代码:

UserEntity.java

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
@Entity
@DynamicUpdate
@Table(name = "UserEntity", uniqueConstraints = {
@UniqueConstraint(columnNames = "ID"),
@UniqueConstraint(columnNames = "EMAIL") })
public class UserEntity implements Serializable {
public UserEntity(String email, String firstName, String lastName) {
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
}

private static final long serialVersionUID = -1798070786993154676L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Integer userId;

@Column(name = "EMAIL", unique = true, nullable = false, length = 100)
private String email;

@Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
private String firstName;

@Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
private String lastName;
}

FindController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class FindController {
public String escapeQuotes(String in){
return in.replaceAll("'","''");
}

@RequestMapping("/findUsers")
public void findUsers(@RequestParam(name="name") String name, HttpServletResponse res) throws IOException{
Configuration config = new Configuration();
// Create SessionFactory with MySQL driver
SessionFactory sessionFactory = config.configure().buildSessionFactory();
Session session = sessionFactory.openSession();
List <UserEntity> users = session.createQuery("from UserEntity where FIRST_NAME ='" + escapeQuotes(name) + "'", UserEntity.class).list();
res.getWriter().println("Found " + users.size() + " Users with that name");
}
}

给出了两个java文件,第一个主要是实体的创建,忽略掉。在FindController文件中,sql语句为"from UserEntity where firstName ='" + escapeQuotes(name) + "'",确定为hql注入漏洞。在用户传入的"name"参数,只通过escapeQuotes方法将"'"替换成了"''",和Hibernate处理字符串文字的单引号方法一致,都是对其进行转义。此时可通过转义"'"的方式绕过限制。

构造payload,执行结果: