Java Web 搭建隨筆 1 (Maven)

Before all

坑還好多,先放著當自己的memo和碎碎念,慢慢補完…
這幾天滾了一圈社區文章,發現自己對Java整個Web體系根本不熟…來開發吧XD
正在照這個複現一些有問題的功能並練習patch(主要還是先忙面試)
https://xz.aliyun.com/t/16232
https://xz.aliyun.com/t/16284
社區寶藏蠻多的XD

我最近看到很喜歡的設計:
From DEVCORE Wargame 2024: https://github.com/DEVCORE-Wargame/HITCON-2024/tree/main/challenges/Expressionism/

挖庫挖庫 >W<b
P.S. 初學Java開發 歡迎各位給我建議/指正我w

What, Why, How maven?

Maven是一個方便搭建web服務的一個專案,主要服務 Java,但也支援 C# 等其他語言的部署,只需提供架設檔(pom.xml)就會幫你把服務編譯/打包好,非常之方便~

pom.xml

我是使用spring boot server架設,是一個方便測試/打包的網頁框架!
結構長這樣(我知道還有很多,POM SPEC(Link)):

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
<!--- XML Header --->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<!---Project Informations--->
<groupId>com.example</groupId>
<artifactId>javademo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>CTF JAVA WEB Demo</name>
<description>A simple Spring Boot application in Java</description>

<!---依賴項,類似 python 的 requirments.txt--->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>

<!---Parent Project,我就在這邊定義使用spring boot--->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.3</version>
<relativePath/>
</parent>

<!---我最後沒用的build,一些config資訊---><!---
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.1.3</version>
</plugin>
</plugins>
</build>
--->


</project>

在打滲透時可以特別注意dependencies中是否版本過舊,另外,TomCat 的 Credential 也可能存在pom.xml或者settings.xml中:

1
2
3
4
5
6
7
<servers>
<server>
<id>TomcatServer</id>
<username>tomcat</username>
<password>password</password>
</server>
</servers>

Structure

Spring Boot架構大概長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
├── pom.xml
└── src
   └── main
   ├── java
   │   └── com
   │   └── example
   │   └── javademo
   │   ├── App.java
   │   └── MainController.java
   └── resources
   ├── static
   │   ├── test.txt
   │   └── wifu.gif
   └── templates
   └── index.html

pom.xml就是剛剛的結構,接下來src存source
main代表主結構,resources中的static與templates跟Flask一樣,分別是放靜態檔案及html模板
java資料夾內則是執行的程式碼,App.java跟MainController.java不是一定要這樣命名,但我覺得這比較能體現他們功能:
App.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.javademo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

支撐整個 SpringApplication 並定義它(hint: 在java裡,主class必須跟檔名一樣)
而MainController一樣是Spring架設時會自己抓的,控制每個路由要幹嘛

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
package com.example.javademo;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;

import java.io.BufferedReader;
import java.io.InputStreamReader;

@Controller
public class MainController {

@GetMapping("/")
@ResponseBody
public String home() {
return "Hello Whale!";
}

@GetMapping("/template")
public String template(@RequestParam(value = "name", defaultValue = "Whale") String name, Model model) {
model.addAttribute("name", name);
return "index";
}

@GetMapping("/get")
@ResponseBody
public Map<String, String> getParams(@RequestParam Map<String, String> queryParams) {
return queryParams;
}

@PostMapping("/post")
@ResponseBody
public Map<String, String> postParams(@RequestBody Map<String, String> bodyParams) {
return bodyParams;
}

@GetMapping("/cmd")
@ResponseBody
public String executeCommand(@RequestParam(value = "cmd", defaultValue = "whoami") String cmd) {
StringBuilder output = new StringBuilder();
try {
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("<br>");
}
} catch (Exception e) {
return "<h1>Error: " + e.getMessage() + "</h1>";
}
return "<h1>Execution Result:</h1><p>" + output + "</p>";
}


@RequestMapping(value = "/evaluate", method = RequestMethod.POST)
@ResponseBody
public String evaluate(@RequestParam(value = "id", defaultValue = "1+1") String id) {

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

try {
Object result = parser.parseExpression(id).getValue(context);
return id+" = " + result;
} catch (Exception e) {
return "Error: Invalid Expression - " + e.getMessage();
}
}
}

如果有寫其他web app的經驗看應該會很快能點出每個地方在幹嘛(大同小異!):

  • @GetMapping修飾符定義了路由,RequestMethod
  • @ResponseBody則定義了純粹的回顯資料,對於有template的顯示是不需要的
  • @RequestParam則是請求的parameters,預設跟常見的a=1&b=2一樣,用parser取出,不過像我POST區就定義是Map<String, String>,這時就必須以JSON請求

/cmd路由示範了Java裡執行命令並回顯得方法
/evaluate路由則示範了SPeL Injection的漏洞
小筆記:

1
2
${#request}的payload在注入jsp時可以獲得位置和一些敏感資訊  
T(java.lang.Runtime).getRuntime().exec('curl localhost:9999/pwned'):基本RCE Chain

compile & run

Maven下載一樣走apt install
我是用spring boot,要先compile:

1
2
mvn clean compile # 先清除以前的compile
mvn spring-boot:run # 用spring-boot跑

提個外話

Tomcat

Structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
這個結構對於用maven, springboot架設jsp的網站有問題嗎
├── pom.xml
└── src
   └── main
   ├── java
   │   ├── Application.java
   │   └── JspController.java
   ├── resources
   │   └── messages.properties
   └── webapp
   └── WEB-INF
   ├── dispatcher-servlet.xml
   ├── views
   │   ├── application.properties
   │   └── index.jsp
   └── web.xml

dispatcher-servlet.xml(非必要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.example" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
</beans>

但可以方便定義一些內容

application.properties

1
2
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

定義mvc應該怎麼渲染jsp files,而所謂的jsp就像這樣:

1
2
3
4
5
6
7
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head><title>Spring Boot JSP Example</title></head>
<body>
<h1><spring:message code="message.${name}" /></h1>
</body>
</html>

大致可理解成一個支援java表達,更靈活的html template
而上面其實就是一個SPeL Injection的範例
舉例來說,讀取訊息就可以像在src/main/resources/messages.properties寫入:

1
message.whale=I'm flying and eating...  

jsp讀取到message.whale就會顯示這段文字~