Skip to content

Commit cb8b04a

Browse files
authored
添加EvilPot测试靶场:一个专门用于让扫描器产生误报的靶场,编写插件应该尽量避免能在这个靶场扫描出结果 (#1805)
1 parent daeac75 commit cb8b04a

File tree

8 files changed

+351
-1
lines changed

8 files changed

+351
-1
lines changed

Diff for: tests/README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
# 测试靶场
22

3+
## Evil Pot
34

4-
这里依靠社区力量收集了几个常用的靶站,可以通过 docker/docker-compose 一键启用。活动开始后,我们注意到在这个repo 中 https://github.com/c0ny1/vulstudy ,作者已经收集了 12 个靶站,这些靶站基本符合我们的要求,所以后续提交中与该 repo 中靶站有所重复的将不再收录。
5+
`evilpot`目录下是我们实现的一个用于提高插件质量的测试靶场。
6+
7+
`evilpot`集成了一些常见的容易导致扫描器误报的情况,编写插件的过程应该尽量避免能在这个靶场扫描出结果。
8+
9+
## 常用靶场
10+
11+
这里依靠社区力量收集了几个常用的靶站,可以通过 docker/docker-compose 一键启用。活动开始后,我们注意到在这个repo
12+
https://github.com/c0ny1/vulstudy ,作者已经收集了 12 个靶站,这些靶站基本符合我们的要求,所以后续提交中与该 repo
13+
中靶站有所重复的将不再收录。
514

615
已有的靶站列表:
716

Diff for: tests/evilpot/README.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Evil Pot
2+
===
3+
4+
邪恶的罐子
5+
6+
一个专门用于让扫描器产生误报的靶场
7+
8+
编写插件应该尽量避免能在这个靶场扫描出结果
9+
10+
## 默认监听端口
11+
12+
- 8887: evil server 让扫描器产生误报 困难模式
13+
- 普通模式的基础上对所有请求元素进行拆解计算sha1/md5/base64
14+
- 8888: evil server 让扫描器产生误报 普通模式
15+
- 常见状态码
16+
- 常见报错信息
17+
- 常见页面
18+
- 常见登录框
19+
- 常见xml头
20+
- 1-1000的sha1/md5/base64
21+
- 回显完整请求
22+
- 尝试计算请求中的算式
23+
- 尝试进行`sleep``wait for`的执行
24+
- 8889: echo server 回显所有读到的数据

Diff for: tests/evilpot/evil/echo.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package evil
2+
3+
import (
4+
"io"
5+
"log"
6+
"net"
7+
)
8+
9+
func ServeEchoServer(addr string) error {
10+
server, err := net.Listen("tcp", addr)
11+
if err != nil {
12+
return err
13+
}
14+
for {
15+
conn, err := server.Accept()
16+
if err != nil {
17+
log.Println(err)
18+
continue
19+
}
20+
go func() {
21+
_, _ = io.Copy(conn, conn)
22+
}()
23+
}
24+
}

Diff for: tests/evilpot/evil/evil.go

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package evil
2+
3+
import (
4+
"bytes"
5+
"crypto/md5"
6+
"crypto/sha1"
7+
"encoding/base64"
8+
"encoding/hex"
9+
"log"
10+
"net/http"
11+
"net/http/httputil"
12+
"net/url"
13+
"regexp"
14+
"strconv"
15+
"sync"
16+
"time"
17+
18+
"github.com/dengsgo/math-engine/engine"
19+
)
20+
21+
func ServeEvilServer(addr string, hard bool) error {
22+
return http.ListenAndServe(addr, NewEvilServeMux(hard))
23+
}
24+
25+
func NewEvilServeMux(hard bool) *http.ServeMux {
26+
s := http.NewServeMux()
27+
mathRe := regexp.MustCompile(`\d+\s*[-+*/]\s*\d+`)
28+
sleepRe := regexp.MustCompile(`(?i)sleep\((\d+)\)`)
29+
waitForRe := regexp.MustCompile(`(?i)waitfor\s+delay\s+'0:0:(\d+)'`)
30+
s.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
31+
buf := bufPool.Get().(*bytes.Buffer)
32+
defer func() {
33+
buf.Reset()
34+
bufPool.Put(buf)
35+
}()
36+
37+
buf.Write(CommonEvilResponse)
38+
39+
data, err := httputil.DumpRequest(request, true)
40+
if err != nil {
41+
log.Println(err)
42+
}
43+
buf.Write(data)
44+
45+
if hard {
46+
Split(data, SepFunc, func(bytes []byte) bool {
47+
GenEvilContent(buf, bytes)
48+
return true
49+
})
50+
}
51+
52+
unescape, _ := url.PathUnescape(string(data))
53+
unescape, _ = url.QueryUnescape(unescape)
54+
if hard {
55+
Split([]byte(unescape), SepFunc, func(bytes []byte) bool {
56+
GenEvilContent(buf, bytes)
57+
return true
58+
})
59+
}
60+
61+
// 处理 sleep 和 WAITFOR DELAY
62+
sleepMatches := sleepRe.FindAllStringSubmatch(unescape, -1)
63+
for _, match := range sleepMatches {
64+
if len(match) > 1 {
65+
sleepTime, _ := strconv.Atoi(match[1])
66+
if sleepTime > 50 {
67+
time.Sleep(time.Millisecond * time.Duration(sleepTime))
68+
} else {
69+
time.Sleep(time.Second * time.Duration(sleepTime))
70+
}
71+
}
72+
}
73+
74+
waitForMatches := waitForRe.FindAllStringSubmatch(unescape, -1)
75+
for _, match := range waitForMatches {
76+
if len(match) > 1 {
77+
waitTime, _ := strconv.Atoi(match[1])
78+
if waitTime > 50 {
79+
time.Sleep(time.Millisecond * time.Duration(waitTime))
80+
} else {
81+
time.Sleep(time.Second * time.Duration(waitTime))
82+
}
83+
}
84+
}
85+
86+
for _, expr := range mathRe.FindAllString(unescape, -1) {
87+
r, err := engine.ParseAndExec(expr)
88+
if err != nil {
89+
log.Println(err)
90+
continue
91+
}
92+
GenEvilContent(buf, []byte(strconv.Itoa(int(r))))
93+
}
94+
95+
_, _ = writer.Write(buf.Bytes())
96+
})
97+
return s
98+
}
99+
100+
var bufPool = sync.Pool{New: func() any {
101+
return bytes.NewBuffer(nil)
102+
}}
103+
104+
func GenEvilContent(dst *bytes.Buffer, data []byte) {
105+
dst.Write(data)
106+
hashMD5 := md5.Sum(data)
107+
dst.Write(hashMD5[:])
108+
dst.WriteString(" ")
109+
dst.WriteString(hex.EncodeToString(hashMD5[:]))
110+
dst.WriteString(" ")
111+
dst.WriteString(base64.StdEncoding.EncodeToString([]byte(hex.EncodeToString(hashMD5[:]))))
112+
dst.WriteString(" ")
113+
hashSha1 := sha1.Sum(data)
114+
dst.Write(hashSha1[:])
115+
dst.WriteString(hex.EncodeToString(hashSha1[:]))
116+
dst.WriteString(" ")
117+
dst.WriteString(base64.StdEncoding.EncodeToString([]byte(hex.EncodeToString(hashSha1[:]))))
118+
dst.WriteString(" ")
119+
dst.WriteString(base64.StdEncoding.EncodeToString(data))
120+
dst.WriteString(" ")
121+
}
122+
123+
// CommonEvilResponse
124+
// 常见md5/sha1/base64 (1-1000的数字)
125+
// 常见登录表单
126+
// 常见错误信息
127+
var CommonEvilResponse = []byte(`<html>
128+
<head>
129+
<title>Level1</title>
130+
<title>OA系统</title>
131+
</head>
132+
133+
<center><h1>Level1</h1></center>
134+
135+
<header>
136+
<h1>OA系统</h1>
137+
<nav>
138+
<ul>
139+
<li><a href="#">首页</a></li>
140+
<li><a href="#">通知公告</a></li>
141+
<li><a href="#">个人中心</a></li>
142+
<li><a href="#">退出登录</a></li>
143+
</ul>
144+
</nav>
145+
</header>
146+
147+
<main>
148+
<section class="banner">
149+
<h2>欢迎使用OA系统</h2>
150+
<p>这是一款集办公、管理、协同于一体的企业级应用软件。</p>
151+
</section>
152+
153+
<form action="/login" method="POST">
154+
<div>
155+
<label for="username">用户名:</label>
156+
<input type="text" id="username" name="username" required>
157+
</div>
158+
<div>
159+
<label for="password">密码:</label>
160+
<input type="password" id="password" name="password" required>
161+
</div>
162+
<button type="submit">登录</button>
163+
</form>
164+
165+
<section class="news">
166+
<h2>新闻动态</h2>
167+
<ul>
168+
<li><a href="#">公司年会隆重举行</a></li>
169+
<li><a href="#">财务部门完成年度结算</a></li>
170+
<li><a href="#">研发部门推出新产品</a></li>
171+
</ul>
172+
</section>
173+
174+
<section class="notice">
175+
<h2>通知公告</h2>
176+
<ul>
177+
<li><a href="#">关于2022年春节放假的通知</a></li>
178+
<li><a href="#">办公室搬迁通知</a></li>
179+
</ul>
180+
</section>
181+
</main>
182+
183+
<footer>
184+
<p>&copy; 2023 OA系统 版权所有</p>
185+
</footer>
186+
187+
<h1>Oops! Something went wrong.</h1>
188+
<p>We're sorry, but an error has occurred while processing your request. Please try again later.</p>
189+
<p>Error Code: 400</p>
190+
<p>Error Code: 401</p>
191+
<p>Error Code: 403</p>
192+
<p>Error Code: 404</p>
193+
<p>Error Code: 500</p>
194+
<p>Error Code: 501</p>
195+
<p>Error Code: 502</p>
196+
<p>Error Code: 503</p>
197+
{
198+
"error": {
199+
"code": 404,
200+
"message": "未找到请求的资源",
201+
"details": "请检查您的请求URL是否正确,并确保所请求的资源存在。"
202+
}
203+
}
204+
</body>
205+
</html>
206+
<!-- a padding to disable MSIE and Chrome friendly error page -->
207+
<!-- a padding to disable MSIE and Chrome friendly error page -->
208+
<!-- a padding to disable MSIE and Chrome friendly error page -->
209+
<!-- a padding to disable MSIE and Chrome friendly error page -->
210+
<!-- a padding to disable MSIE and Chrome friendly error page -->
211+
<!-- a padding to disable MSIE and Chrome friendly error page -->
212+
<?xml version="1.0" encoding="UTF-8"?>
213+
<?xml version="1.1" encoding="UTF-8"?>`)
214+
215+
func init() {
216+
buf := bytes.NewBuffer(nil)
217+
buf.Write(CommonEvilResponse)
218+
for i := 0; i < 1000; i++ {
219+
GenEvilContent(buf, []byte(strconv.Itoa(i)))
220+
}
221+
buf.WriteString("\nroot:x:0:0:root:/root:/bin/bash\n")
222+
CommonEvilResponse = buf.Bytes()
223+
}

Diff for: tests/evilpot/evil/util.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package evil
2+
3+
var SepFunc = func(b byte) bool {
4+
if 48 <= b && b <= 57 {
5+
return false
6+
}
7+
if b >= 128 {
8+
return false
9+
}
10+
if b >= 65 && b <= 90 {
11+
return false
12+
}
13+
if b >= 97 && b <= 122 {
14+
return false
15+
}
16+
return true
17+
}
18+
19+
func Split(data []byte, sep func(b byte) bool, handler func([]byte) bool) {
20+
if sep == nil {
21+
sep = SepFunc
22+
}
23+
visible := 0
24+
invisible := 0
25+
for i := 0; i < len(data); i++ {
26+
if !sep(data[i]) {
27+
continue
28+
}
29+
invisible = i
30+
if invisible == visible {
31+
visible++
32+
continue
33+
}
34+
b := data[visible:invisible]
35+
if !handler(b) {
36+
return
37+
}
38+
visible = invisible + 1
39+
}
40+
if visible < len(data) {
41+
handler(data[visible:])
42+
}
43+
return
44+
}

Diff for: tests/evilpot/go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/chaitin/xray/tests/evilpot
2+
3+
go 1.21
4+
5+
require github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149

Diff for: tests/evilpot/go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149 h1:TkVfb0s14IUHDGvjQfq3f0PZnV1zh609did4DrnD4q4=
2+
github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149/go.mod h1:zkR27k4K0I8FS6rkEd8qBhPeS8i3X2FKfvSPdF64OpQ=

Diff for: tests/evilpot/main.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
7+
"github.com/chaitin/xray/tests/evilpot/evil"
8+
)
9+
10+
func main() {
11+
evilHardAddr := flag.String("evil-hard", ":8887", "evil server 困难模式 监听地址")
12+
evilAddr := flag.String("evil", ":8888", "evil server 监听地址")
13+
echoAddr := flag.String("echo", ":8889", "echo server 监听地址")
14+
flag.Parse()
15+
go func() { log.Fatalln(evil.ServeEvilServer(*evilHardAddr, true)) }()
16+
go func() { log.Fatalln(evil.ServeEvilServer(*evilAddr, false)) }()
17+
go func() { log.Fatalln(evil.ServeEchoServer(*echoAddr)) }()
18+
select {}
19+
}

0 commit comments

Comments
 (0)