mirror of
https://github.com/Li4n0/revsuit.git
synced 2026-09-22 06:40:43 +08:00
Compare commits
25
Commits
v0.1.4-beta
...
v0.1.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
206808e83a | ||
|
|
a248c6e39e | ||
|
|
5e2020efff | ||
|
|
c0bda707b7 | ||
|
|
ffc4318725 | ||
|
|
8023a9b021 | ||
|
|
56153510e1 | ||
|
|
be7343191d | ||
|
|
b1773ee5e9 | ||
|
|
18707cec47 | ||
|
|
993d3867ee | ||
|
|
d4d7435265 | ||
|
|
70f17453fd | ||
|
|
ef38cbc790 | ||
|
|
6205772a59 | ||
|
|
807186be61 | ||
|
|
1c625dd881 | ||
|
|
30194479d1 | ||
|
|
ddeced3540 | ||
|
|
dc43b6973a | ||
|
|
d1013c2416 | ||
|
|
f7be2bc2e6 | ||
|
|
42d1e35913 | ||
|
|
74f512d214 | ||
|
|
b945069117 |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Environments**
|
||||
Your operating system, such as `Mac OS Catalina 10.15.7`.
|
||||
|
||||
**RevSuit Version**
|
||||
Versions of RevSuit with bugs.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the new feature you expect to add**
|
||||
A clear and concise description.
|
||||
|
||||
**Why do you want to add this feature**
|
||||
A clear and concise description of why you need the feature, such as in what kind of usage scenarios it would be more convenient to have a certain feature.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -12,14 +12,11 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.16.0'
|
||||
- name: compile
|
||||
env:
|
||||
CGO_ENABLE: 1
|
||||
CGO_LDFLAGS: -static
|
||||
run: |
|
||||
GOARCH="amd64" go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_linux_amd64" ./cmd/revsuit/revsuit.go
|
||||
GOARCH="386" go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_linux_i386" ./cmd/revsuit/revsuit.go
|
||||
GOARCH="arm" go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_linux_arm" ./cmd/revsuit/revsuit.go
|
||||
GOARCH="arm64" go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_linux_arm64" ./cmd/revsuit/revsuit.go
|
||||
GOARCH="amd64" go build -v -ldflags="-s -w -extldflags=-static" -tags netgo -trimpath -o "bin/revsuit_linux_amd64" ./cmd/revsuit/revsuit.go
|
||||
# GOARCH="386" go build -v -ldflags="-s -w -extldflags=-static" -tags netgo -trimpath -o "bin/revsuit_linux_i386" ./cmd/revsuit/revsuit.go
|
||||
# GOARCH="arm" go build -v -ldflags="-s -w -extldflags=-static" -tags netgo -trimpath -o "bin/revsuit_linux_arm" ./cmd/revsuit/revsuit.go
|
||||
# GOARCH="arm64" go build -v -ldflags="-s -w -extldflags=-static" -tags netgo -trimpath -o "bin/revsuit_linux_arm64" ./cmd/revsuit/revsuit.go
|
||||
- name: Upload the artifacts
|
||||
uses: li4n0/upload-release-action@v2
|
||||
with:
|
||||
@@ -37,11 +34,9 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.16.0'
|
||||
- name: compile
|
||||
env:
|
||||
CGO_ENABLE: 1
|
||||
run: |
|
||||
GOARCH="amd64" go build -v -ldflags="-s -w" -trimpath -o "bin/revsuit_darwin_amd64" ./cmd/revsuit/revsuit.go
|
||||
GOARCH="arm64" go build -v -ldflags="-s -w" -trimpath -o "bin/revsuit_darwin_arm64" ./cmd/revsuit/revsuit.go
|
||||
# GOARCH="arm64" go build -v -ldflags="-s -w" -trimpath -o "bin/revsuit_darwin_arm64" ./cmd/revsuit/revsuit.go
|
||||
- name: Upload the artifacts
|
||||
uses: li4n0/upload-release-action@v2
|
||||
with:
|
||||
@@ -59,13 +54,10 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.16.0'
|
||||
- name: compile
|
||||
env:
|
||||
CGO_ENABLE: 1
|
||||
CGO_LDFLAGS: -static
|
||||
run: |
|
||||
env GOARCH=amd64 go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_windows_amd64.exe" ./cmd/revsuit/revsuit.go
|
||||
env GOARCH=386 go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_windows_i386.exe" ./cmd/revsuit/revsuit.go
|
||||
env GOARCH=arm go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_windows_arm.exe" ./cmd/revsuit/revsuit.go
|
||||
# env GOARCH=386 go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_windows_i386.exe" ./cmd/revsuit/revsuit.go
|
||||
# env GOARCH=arm go build -v -ldflags="-s -w -extldflags=-static" -trimpath -o "bin/revsuit_windows_arm.exe" ./cmd/revsuit/revsuit.go
|
||||
- name: Upload the artifacts
|
||||
uses: li4n0/upload-release-action@v2
|
||||
with:
|
||||
|
||||
+4
-3
@@ -2,9 +2,10 @@
|
||||
|
||||
# local files
|
||||
qqwry.dat
|
||||
revsuit.db
|
||||
revsuit.db-shm
|
||||
revsuit.db-wal
|
||||
GeoLite2-City.mmdb
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
|
||||
# local config
|
||||
config.yaml*
|
||||
|
||||
+46
-2
@@ -1,7 +1,51 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.1.7](https://github.com/Li4n0/revsuit/compare/v0.1.6...v0.1.7) (2021-07-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **http:** fix the bug that http record url search does not work ([ffc4318](https://github.com/Li4n0/revsuit/commit/ffc4318725017c7eda7ec11ca67d5de16ad1e8a8))
|
||||
* **mysql:** fix the bug that crashes when mysql port conflicts ([c0bda70](https://github.com/Li4n0/revsuit/commit/c0bda707b750b7858fdd4efc414b726ee37e71da))
|
||||
|
||||
### [0.1.6](https://github.com/Li4n0/revsuit/compare/v0.1.5...v0.1.6) (2021-06-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** fix the bug that the prompt may not be output when generating config file ([5615351](https://github.com/Li4n0/revsuit/commit/56153510e11783c08843cbb8b0f827d5f8567842))
|
||||
* **ipinfo:** fix a potential panic when downloading ip location database ([#33](https://github.com/Li4n0/revsuit/issues/33)) ([b1773ee](https://github.com/Li4n0/revsuit/commit/b1773ee5e9ebd8c8be72f315981268992c08b3f1))
|
||||
|
||||
### [0.1.5](https://github.com/Li4n0/revsuit/compare/v0.1.5-beta...v0.1.5) (2021-06-22 公开该项目以庆祝毕业,愿工作多年后的我,归来时仍是少年)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **notice:** fix the buf of notice with null record field ([#31](https://github.com/Li4n0/revsuit/issues/31)) ([ef38cbc](https://github.com/Li4n0/revsuit/commit/ef38cbc790f69716a335167321e1c7c8bdee2e41))
|
||||
|
||||
### [0.1.5-beta](https://github.com/Li4n0/revsuit/compare/v0.1.4-beta-fix-1...v0.1.5-beta) (2021-06-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ipinfo:** support GeoIP ([#26](https://github.com/Li4n0/revsuit/issues/26)) ([dc43b69](https://github.com/Li4n0/revsuit/commit/dc43b6973a5ac98e439d1353102ade2029b1d382))
|
||||
* **platform:** support custom admin path prefix ([#25](https://github.com/Li4n0/revsuit/issues/25)) ([f7be2bc](https://github.com/Li4n0/revsuit/commit/f7be2bc2e67841178e9316995999b20f80a49df7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** fix the bug of autoRefresh ([#27](https://github.com/Li4n0/revsuit/issues/27)) ([d1013c2](https://github.com/Li4n0/revsuit/commit/d1013c2416c04bd72b9cbb0c7e6b4b3f2e717837))
|
||||
* **frontend:** fix the frontend display bugs ([#28](https://github.com/Li4n0/revsuit/issues/28)) ([ddeced3](https://github.com/Li4n0/revsuit/commit/ddeced354042e0be6db5dd4feffa13ef22db231f))
|
||||
|
||||
### [0.1.4-beta-fix-1](https://github.com/Li4n0/revsuit/compare/v0.1.4-beta...v0.1.4-beta-fix-1) (2021-05-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** fix the problem of autoRefresh failure ([#24](https://github.com/Li4n0/revsuit/issues/24)) ([b945069](https://github.com/Li4n0/revsuit/commit/b945069117fec6eb5de88557da5d4c2c996cfd90))
|
||||
* **http:** fix the problem that ip_header setting does not take effect ([#23](https://github.com/Li4n0/revsuit/issues/23)) ([74f512d](https://github.com/Li4n0/revsuit/commit/74f512d2140fb97128acf56be803d1bd3b888fa3))
|
||||
|
||||
### [0.1.4-beta](https://github.com/Li4n0/revsuit/compare/v0.1.3-beta-fix1...v0.1.4-beta) (2021-05-27)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# RevSuit - A Flexible and Powerful Reverse Connection Platform
|
||||
|
||||
English |[简体中文](./README.zh-CN.md)
|
||||
|
||||
## Overview
|
||||
|
||||
RevSuit is a flexible and powerful reverse connection platform designed for receiving connection from target host in
|
||||
@@ -7,13 +9,13 @@ penetration. It currently supports HTTP, DNS, RMI, MySQL and FTP protocols.
|
||||
|
||||
Flexible:
|
||||
|
||||
1. Revsuit can set flexible rules to capture different connections.
|
||||
1. RevSuit can set flexible rules to capture different connections.
|
||||
2. Deploy and run via binaries without worrying about nasty dependency errors.
|
||||
|
||||
Powerful:
|
||||
|
||||
1. Revsuit allows users to customize the response for different connections through rules and supports dynamic response
|
||||
generation using template variables.
|
||||
1. Allows to customize the response for different connections through rules and supports dynamic response generation
|
||||
using template variables.
|
||||
2. Support a variety of protocols, and support the in-depth utilization of some protocols, such as DNS rebinding, MySQL
|
||||
Load Local Data, JDBC Deserialize Exploit, FTP Passive Mode SSRF, etc.
|
||||
3. Native support for use with scanners.
|
||||
@@ -24,13 +26,16 @@ Powerful:
|
||||
|
||||
### Preparation
|
||||
|
||||
Download the latest release firstly.
|
||||
Download the latest release firstly. For the time being, only the binary for amd64 architecture is available for
|
||||
download, if you need to use it in other architectures, you can compile it yourself.
|
||||
|
||||
RevSuit will generate default configuration file on first run. Modify the configuration file according to your needs,
|
||||
then re-run.
|
||||
|
||||
To confirm the location of the connection IP, if there is no qqwry.dat in the current directory or its modification time
|
||||
is more than 5 days ago. RevSuit will download it.(Support for GeoIP is in the planning stages).
|
||||
In order to confirm the IP location, it needs to use the IP location database. `QQwry` is used as the data source by
|
||||
default, you can also modify the configuration to use `GeoIP`. If the selected database is not available in the current
|
||||
directory or the database is updated for more than a week, RevSuit will automatically download the latest database. If
|
||||
the download fails, the `IpArea` field will always be null.
|
||||
|
||||
```bash
|
||||
$ ./revsuit
|
||||
@@ -63,8 +68,8 @@ A few notes are as follows:
|
||||
2. `FlagFormat` uses regular expression syntax, and for different protocols, the fields matched by flagFormat are
|
||||
different, you can check the corresponding hints to get details when creating rules.
|
||||
3. Rules with high rank will take effect first.
|
||||
4. For different protocol rules, you can make different custom configurations, and for some protocols, template
|
||||
variables can be used.
|
||||
4. For different protocol rules, you can make different custom responses, and for some protocols, template variables can
|
||||
be used.
|
||||
5. You can set the named grouping of regular expressions in `flagFormat`, and the result of the matching grouping will
|
||||
also be used as a template variable.
|
||||
|
||||
@@ -127,7 +132,7 @@ RevSuit was split from my scanner project, so its native support works with scan
|
||||
|
||||
From RevSuit's perspective, we call a scanner a client.
|
||||
|
||||
#### Create Connection
|
||||
#### Establish connection
|
||||
|
||||
RevSuit
|
||||
uses [HTTP Server-sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
|
||||
@@ -143,8 +148,8 @@ example.
|
||||
|
||||
#### Multi-client
|
||||
|
||||
As shown above, RevSuit supports multiple clients, and each client in the connected state receives a push of `flag`, so
|
||||
distributed scanning can be supported.
|
||||
As shown above, RevSuit supports multiple clients, and each client **in the connected state** receives a push of `flag`,
|
||||
so distributed scanning can be supported.
|
||||
|
||||
#### Temporary storage queue
|
||||
|
||||
@@ -154,15 +159,15 @@ helpful for discovering delay-triggered vulnerabilities.)
|
||||
|
||||
#### Use flagGroup
|
||||
|
||||
`FlagGroup` is the content matched by the anonymous group in the `flagFormat` field of the rule. The platform will check
|
||||
the content matched in the grouping,and the flag is only pushed to the client when then content(`flagGroup`) captured
|
||||
for the first time.
|
||||
|
||||
In a real-world vulnerability scanning scenario, you may send a large number of different payloads for a single
|
||||
vulnerability point, and they may all be valid, which can result in the backlink platform receiving many requests, yet
|
||||
they are caused by the same vulnerability. If you don't want the client to receive so many `flags` for the same
|
||||
vulnerability, you can take advantage of the `flagGroup` feature of rule's flagFormat.
|
||||
|
||||
`FlagGroup` is the content matched by the anonymous group in the `flagFormat` field of the rule. The platform will check
|
||||
the content matched in the grouping,and the flag is only pushed to the client when then content(`flagGroup`) captured
|
||||
for the first time.
|
||||
|
||||
For example, SSRF scanning.
|
||||
|
||||
Firstly create a rule like the following:
|
||||
@@ -181,23 +186,22 @@ http:
|
||||
|
||||
Suppose our target is `https://www.testvuln.com?url=api.com&p=useless`, and for SSRF we have 5 payloads. The final
|
||||
request sent by the scanner may
|
||||
be `['https://www.testvuln.com?url=http://revsuit.com/ssrfa98oni1&p=useless, ... ,'https://www.testvuln.com?url=//revsuit.com/ssrfa98oni5&p=useless`']
|
||||
be `['https://www.testvuln.com?url=http://revsuit.com/ssrfa98oni1&p=useless','https://www.testvuln.com?url=http://revsuit.com/ssrfa98oni2&p=useless', ... ,'https://www.testvuln.com?url=//revsuit.com/ssrfa98oni5&p=useless`']
|
||||
. They may all attack successfully.
|
||||
|
||||
However, because anonymous grouping is used in the rule's `flagFormat`, The platform will query the
|
||||
connected `flagGroup`, in this case `ssrfa98oni`, and push `flag` to the client only at its first appearance, so the
|
||||
client will only receive one flag:`ssrfa98oni1`. But this is enough, because it already proves that the `url` parameter
|
||||
of the target is vulnerable.
|
||||
client will only receive one flag:`ssrfa98oni1`. It already proves that the `url` parameter of the target is vulnerable.
|
||||
|
||||
### Multi-module collocation
|
||||
|
||||
In actual penetration testing scenarios, certain tasks can be done easily and quickly by combining and matching various
|
||||
modules of RevSuit. The following is an example of a blind XXE scenario, showing how to use RevSuit's HTTP and FTP
|
||||
modules of RevSuit. The following is an example of a blind XXE in Java Web, showing how to use RevSuit's HTTP and FTP
|
||||
modules, combined with template variables, to quickly complete a port scan.
|
||||
|
||||
First use create an HTTP rule to return evil.dtd, customize the response to the contents of dtd so that it goes to
|
||||
connect to RevSuit's FTP service, and use template variables to pass the Host and Port to be scanned to FTP via FTP's
|
||||
user and password.
|
||||
First create an HTTP rule to return evil.dtd, customize the response to the contents of dtd so that it goes to connect
|
||||
to RevSuit's FTP service, and use template variables to pass the Host and Port to be scanned to FTP via FTP's user and
|
||||
password.
|
||||
|
||||

|
||||
|
||||
@@ -223,14 +227,16 @@ and 8080 are open.
|
||||
|
||||
A more detailed wiki is being prepared, you can explore by yourself before it.
|
||||
|
||||
## Advice and Communication
|
||||
## Feedback, Suggestions and Communication
|
||||
|
||||
Submit an issue or contact me through Weixin: `Li4n06`
|
||||
Submit an issue or contact me through Weixin: `TGk0bjA2Cg==`
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
### Reference
|
||||
|
||||
This project draws on the code of the following outstanding projects:
|
||||
|
||||
* [https://github.com/rmb122/rogue_mysql_server](https://github.com/rmb122/rogue_mysql_server)
|
||||
* [https://github.com/256dpi/newdns](https://github.com/256dpi/newdns)
|
||||
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
# RevSuit - A Flexible and Powerful Reverse Connection Platform
|
||||
|
||||
简体中文 |[English](./README.md)
|
||||
|
||||
## 简介
|
||||
|
||||
RevSuit 是一款灵活并且强大的反连平台。目前支持 HTTP、DNS、RMI、MySQL 和 FTP 协议。
|
||||
|
||||
灵活:
|
||||
|
||||
1. 通过灵活的规则来捕获连接
|
||||
2. 通过二进制文件部署和运行而无需担心讨厌的依赖错误
|
||||
|
||||
强大:
|
||||
|
||||
1. 可以通过规则,为不同类型的连接自定义响应,并且可以使用模板变量实现动态响应。
|
||||
2. 支持的协议数量相对较多,并且支持其中的部分协议的进一步利用,如 DNS重绑定、MySQL客户端文件读取和 JDBC 反序列化、FTP 被动模式 SSRF 等
|
||||
3. 原生支持扫描器
|
||||
4. 支持发送通知到流行的办公软件
|
||||
5. 其它有趣或贴心的功能
|
||||
|
||||
## 基础使用
|
||||
|
||||
### 安装
|
||||
|
||||
在 Releases 里下载最新版本的 RevSuit。暂时只提供 amd64 架构下的二进制文件下载,如需要在其它架构下使用,可以自行编译。
|
||||
|
||||
RevSuit 首次运行时将会生成配置文件模板,你需要编辑其中的内容然后重新运行。
|
||||
|
||||
为了确认连接 IP 的归属地,RevSuit 需要依赖 IP 归属地数据库。默认情况下使用纯真 IP 库,你也可以在配置文件中指定使用 GeoIP。如果当前目录下没有被选中的数据库文件,或者数据库文件更新的时间大于一周,RevSuit
|
||||
将会自动下载最新版的数据库文件到本地。如果下载失败,连接记录的`IpArea`字段将始终为空。
|
||||
|
||||
```bash
|
||||
$ ./revsuit
|
||||
2021/05/16 22:55:10 [ INFO] Downloading qqwry.dat...
|
||||
|
||||
____ _____ _ __
|
||||
/ __ \___ _ __/ ___/__ __(_) /_
|
||||
/ /_/ / _ \ | / /\__ \/ / / / / __/
|
||||
/ _, _/ __/ |/ /___/ / /_/ / / /_
|
||||
/_/ |_|\___/|___//____/\__,_/_/\__/
|
||||
vBeta0.1
|
||||
https://revsuit.pro
|
||||
2021/05/16 22:55:22 [ INFO] Starting HTTP Server at :80, token:your_token
|
||||
2021/05/16 22:55:22 [ INFO] Start to listen FTP PASV port at :2020, PasvIP is 10.9.8.7
|
||||
2021/05/16 22:55:22 [ INFO] Starting FTP Server at :21
|
||||
2021/05/16 22:55:22 [ INFO] Starting MySQL Server at :3306
|
||||
2021/05/16 22:55:22 [ INFO] Starting RMI Server at :1099
|
||||
2021/05/16 22:55:22 [ INFO] Starting DNS Server at :53
|
||||
```
|
||||
|
||||
在启动之后,你可以通过访问HTTP Server的`/revsuit/admin/`路径,并输入 token 来进入控制面板。
|
||||
|
||||
### 创建规则与接收连接
|
||||
|
||||
这里以创建一个HTTP规则作为例子:
|
||||
|
||||
一些需要注意的内容如下:
|
||||
|
||||
1. 规则的 `name` 和 `flagFormat` 字段是唯一的。
|
||||
2. `flagFormat` 使用正则表达式语法。对于不同的协议,其匹配的字段也是不同的。你可以在创建规则的时候查看对应的帮助信息来获知该协议的哪个字段是会被匹配的。
|
||||
3. `rank` 决定了规则的优先级,如果一个请求同时满足多条规则,只有 `rank` 值最大的那条规则会生效。
|
||||
4. 对于不同的协议,你可以设置不同的响应,部分协议可以使用模板变量来动态生成响应。
|
||||
5. 你可以在 `flagFormat` 中使用正则表达式中的命名分组,命名分组匹配的结果将会成为自定义的模板变量。
|
||||
|
||||
如下图所示,我们创建一个名为 `test_create_rule` 的HTTP协议的规则,其中使用了内置的模板变量和自定义的模板变量:
|
||||
|
||||

|
||||
|
||||
然后发送一个满足这条规则的请求来进行测试,可以看到响应的内容中各个模板变量都获取到了对应的值:
|
||||
|
||||

|
||||
|
||||
这条请求同时会被记录在平台:
|
||||
|
||||

|
||||
|
||||
### 通知
|
||||
|
||||
通过配置 Webhook 地址并打开对应规则的 `Notice` 开关,你可以通过办公软件接收新连接的通知。目前支持 钉钉、企业微信、飞书 和 slack。(计划会后续支持Discord 和 Telegram)
|
||||
|
||||
### 导入/导出规则
|
||||
|
||||
如果你想要迁移平台或者清除数据,重新创建规则可能是一件麻烦事。因此 RevSuit 支持导入和导出规则。
|
||||
|
||||
该功能位于 `Settings>RULES` 选项卡,如下所示,规则将会被以 yaml 的格式进行导入和导出:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
- name: test_create_rule
|
||||
flag_format: (?P<what>\w+)\?
|
||||
rank: 0
|
||||
push_to_client: false
|
||||
notice: false
|
||||
response_status_code: "302"
|
||||
response_headers:
|
||||
Location: ${query.url}
|
||||
response_body: ${header.say} ${what}
|
||||
- name: other_rule
|
||||
flag_format: other
|
||||
rank: 1
|
||||
push_to_client: false
|
||||
notice: true
|
||||
response_status_code: "200"
|
||||
response_headers: { }
|
||||
response_body: Hello Revsuit!
|
||||
dns:
|
||||
... ...
|
||||
```
|
||||
|
||||
## 高级功能
|
||||
|
||||
### 与扫描器适配
|
||||
|
||||
RevSuit 拆分自我的扫描器项目,因此它原生支持搭配扫描器使用。
|
||||
|
||||
从 RevSuit 的视角,我们称扫描器为客户端。
|
||||
|
||||
#### 建立通信管道
|
||||
|
||||
RevSuit
|
||||
使用 [HTTP Server-sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
|
||||
(SSE) 与客户端建立一条单向通信管道。
|
||||
|
||||
通信管道的API为 `/revsuit/api/events?message`。客户端只需携带 `Token: your token` 请求头,然后请求这个 API 来建立通信管道。这样当 RevSuit
|
||||
捕获新连接的时候,匹配到的 `flag` 就会通过这条管道传递给客户端。
|
||||
|
||||

|
||||
|
||||
这里是一个使用 Go 语言的 sse 库编写客户端的[简单例子](https://gist.github.com/Li4n0/21aa0bec2d626114a729ca2677efb05a) 。
|
||||
|
||||
#### 多客户端
|
||||
|
||||
如上一节的图片里所展示的,RevSuit 支持多个客户端,每个在**连接状态**的客户端都会接收到 `flag` 的推送,因此分布式扫描也是支持的。
|
||||
|
||||
#### 暂存队列
|
||||
|
||||
当没有客户端连接的时候,RevSuit 会把 `flag` 暂时存储到队列里,当有客户端连接的时候再进行推送。因此你不必担心因为客户端与 RevSuit 断开连接就错过漏洞。(这对发现延时触发的漏洞尤为有帮助)
|
||||
|
||||
#### 使用flagGroup
|
||||
|
||||
在真实的漏洞扫描情景中,对于一个漏洞点,往往会发送许多不同的 payload,它们可能全部是有效 payload,这会导致 RevSuit
|
||||
收到很多个连接,然而这些连接背后的漏洞其实是同一个。如果你不想客户端收到代表同一个漏洞的 `flag`
|
||||
,你可以使用 `flagGroup`。
|
||||
|
||||
`flagGroup` 是被规则中的 `flagFormat` 中的匿名分组所匹配到的内容。RevSuit 将会检查 `flagGroup` 是否已经在之前的连接中被捕获过,只有当 `flagGroup`
|
||||
是首次出现时,才会推送 `flag`
|
||||
到客户端。
|
||||
|
||||
以针对 SSRF 漏洞扫描为例:
|
||||
|
||||
首先我们创建一个如下的规则:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
- name: ssrf
|
||||
flag_format: (ssrf[a-z0-9]{6})[0-9]{1,3}
|
||||
rank: 0
|
||||
push_to_client: false
|
||||
notice: false
|
||||
response_status_code: "200"
|
||||
response_headers: { }
|
||||
response_body: "Here is a SSRF!"
|
||||
```
|
||||
|
||||
假设我们的目标是 `https://www.testvuln.com?url=api.com&p=useless`,并且针对 SSRF 漏洞,我们有5个
|
||||
payload,最后被扫描器发出的请求是:`['https://www.testvuln.com?url=http://revsuit.com/ssrfa98oni1&p=useless','https://www.testvuln.com?url=http://revsuit.com/ssrfa98oni2&p=useless', ... ,'https://www.testvuln.com?url=//revsuit.com/ssrfa98oni5&p=useless`']
|
||||
,它们可能全部攻击成功.
|
||||
|
||||
然而,因为我们在规则的 `flagFormat` 中使用了匿名分组,RevSuit 将会在数据库中查询连接的 `flagGroup`,在这个例子里也就是 `ssrfa98oni` ,只有当它是第一次出现时,才会向客户端推送 `flag`
|
||||
,所以客户端只会收到 `ssrfa98oni1` 这一个 `flag` ,这足以确认这个请求的 URL 参数存在漏洞了。
|
||||
|
||||
### 多模块联动
|
||||
|
||||
在实际的渗透测试场景中,一些特定的操作可以通过 RevSuit 多个模块配合,快速、简单地完成。下面以一个 Java Web 环境中 盲XXE 漏洞利用场景为例,展示如何使用 RevSuit 的 HTTP 和 FTP
|
||||
模块,配合模板变量,快速完成内网端口扫描。
|
||||
|
||||
首先创建一个 HTTP 规则来返回 evil.dtd, 使被攻击的目标去连接 RevSuit 的 FTP 模块,并且通过设置 FTP 连接的用户名和密码为模板变量来传递我们要扫描的 IP 和端口。
|
||||
|
||||

|
||||
|
||||
然后创建一个 FTP 规则,其中 `Pasv Address` 的 Host 和 Port 通过模板变量从 FTP 连接的用户名和密码动态获取。
|
||||
|
||||

|
||||
|
||||
然后使用 BurpSuit 即可进行扫描,通过设置 evil.dtd 请求中的 host 和 post 参数,来指定要扫描的目标端口:
|
||||
|
||||

|
||||
|
||||
运行效果如下:
|
||||
|
||||

|
||||
|
||||
因为在 FTP被动模式 中如果 `Passive Address` 是不可达的, FTP 的连接会中断,因此我们可以通过检查 FTP 的连接是否正常退出,来判断端口是否开放。对于上面这个例子,我们成功的探测出 8005 端口和 8080
|
||||
端口是开放的。
|
||||
|
||||
## 更多用法
|
||||
|
||||
一个更详细的 wiki 正在准备中,在此之前你可以自己探索...
|
||||
|
||||
## 反馈、建议与交流
|
||||
|
||||
提交 Issue 或者通过微信联系我`TGk0bjA2Cg==`
|
||||
|
||||
## 致谢
|
||||
|
||||
### 引用
|
||||
|
||||
本项目引用了如下优秀项目的代码:
|
||||
|
||||
* [https://github.com/rmb122/rogue_mysql_server](https://github.com/rmb122/rogue_mysql_server)
|
||||
* [https://github.com/256dpi/newdns](https://github.com/256dpi/newdns)
|
||||
|
||||
### 特别感谢
|
||||
|
||||
感谢我的朋友[@E99p1ant](https://github.com/wuhan005/) 在我开发这个项目过程中给予非常多的建议和帮助。
|
||||
|
||||
## 开源许可协议
|
||||
|
||||
@Apache License 2.0
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
.custom-icons-list[data-v-05a908c2] .anticon{margin-right:6px}body[data-v-2f6f0625],html[data-v-2f6f0625]{height:100%;margin:0}.fade-transform-enter-active[data-v-2f6f0625],.fade-transform-leave-active[data-v-2f6f0625]{transition:all .3s;opacity:0}.fade-transform-enter[data-v-2f6f0625],.fade-transform-leave[data-v-2f6f0625]{opacity:0}.fade-transform-enter-to[data-v-2f6f0625]{opacity:0}.ant-menu-item>span>a[data-v-2f6f0625]{color:hsla(0,0%,100%,.65)}.ant-menu-item-selected>span>a[data-v-2f6f0625]{color:#fff}#app[data-v-2f6f0625]{height:100%}#app .trigger[data-v-2f6f0625]{font-size:18px;line-height:64px;padding:0 24px;cursor:pointer;transition:color .3s}#app .trigger[data-v-2f6f0625]:hover{color:#1890ff}#app .logo[data-v-2f6f0625]{height:32px;background:#0a1d2d;margin:16px;text-align:center;font-size:1.2rem;color:#fff;padding-bottom:5px;border-bottom:2px solid #b6befa}.copyright[data-v-2f6f0625]{color:#888;text-align:right;margin-right:1rem}
|
||||
.custom-icons-list[data-v-05a908c2] .anticon{margin-right:6px}body[data-v-1bc6d13e],html[data-v-1bc6d13e]{height:100%;margin:0}.fade-transform-enter-active[data-v-1bc6d13e],.fade-transform-leave-active[data-v-1bc6d13e]{transition:all .3s;opacity:0}.fade-transform-enter[data-v-1bc6d13e],.fade-transform-leave[data-v-1bc6d13e]{opacity:0}.fade-transform-enter-to[data-v-1bc6d13e]{opacity:0}.ant-menu-item>span>a[data-v-1bc6d13e]{color:hsla(0,0%,100%,.65)}.ant-menu-item-selected>span>a[data-v-1bc6d13e]{color:#fff}#app[data-v-1bc6d13e]{min-height:100%}#app .trigger[data-v-1bc6d13e]{font-size:18px;line-height:64px;padding:0 24px;cursor:pointer;transition:color .3s}#app .trigger[data-v-1bc6d13e]:hover{color:#1890ff}#app .logo[data-v-1bc6d13e]{height:32px;background:#0a1d2d;margin:16px;text-align:center;font-size:1.2rem;color:#fff;padding-bottom:5px;border-bottom:2px solid #b6befa}.copyright[data-v-1bc6d13e]{color:#888;text-align:right;margin-right:1rem}
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>RevSuit Control Panel</title><link href="css/chunk-016c45cf.css" rel="prefetch"><link href="css/chunk-0cb5e9e6.css" rel="prefetch"><link href="css/chunk-1da7f23b.css" rel="prefetch"><link href="css/chunk-247d6284.css" rel="prefetch"><link href="css/chunk-330a7e95.css" rel="prefetch"><link href="css/chunk-3d13e56e.css" rel="prefetch"><link href="css/chunk-43748c28.css" rel="prefetch"><link href="css/chunk-49fbda44.css" rel="prefetch"><link href="css/chunk-e039e75a.css" rel="prefetch"><link href="css/chunk-fef965c2.css" rel="prefetch"><link href="js/chunk-016c45cf.js" rel="prefetch"><link href="js/chunk-08f9fbe8.js" rel="prefetch"><link href="js/chunk-0cb5e9e6.js" rel="prefetch"><link href="js/chunk-1da7f23b.js" rel="prefetch"><link href="js/chunk-247d6284.js" rel="prefetch"><link href="js/chunk-330a7e95.js" rel="prefetch"><link href="js/chunk-3d13e56e.js" rel="prefetch"><link href="js/chunk-43748c28.js" rel="prefetch"><link href="js/chunk-49fbda44.js" rel="prefetch"><link href="js/chunk-b241c712.js" rel="prefetch"><link href="js/chunk-e039e75a.js" rel="prefetch"><link href="js/chunk-fef965c2.js" rel="prefetch"><link href="css/app.css" rel="preload" as="style"><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="js/app.js" rel="preload" as="script"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link href="css/app.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but revsuit-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.js"></script><script src="js/app.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>RevSuit Control Panel</title><link href="/revsuit/admin/css/chunk-016c45cf.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-0cb5e9e6.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-193ec343.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-36508a38.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-43748c28.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-46a53d26.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-49fbda44.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-5fb70d83.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-72839f90.css" rel="prefetch"><link href="/revsuit/admin/css/chunk-740736c7.css" rel="prefetch"><link href="/revsuit/admin/js/chunk-016c45cf.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-0cb5e9e6.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-193ec343.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-27186dd0.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-36508a38.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-43748c28.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-46a53d26.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-49fbda44.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-5fb70d83.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-72839f90.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-740736c7.js" rel="prefetch"><link href="/revsuit/admin/js/chunk-b241c712.js" rel="prefetch"><link href="/revsuit/admin/css/app.css" rel="preload" as="style"><link href="/revsuit/admin/css/chunk-vendors.css" rel="preload" as="style"><link href="/revsuit/admin/js/app.js" rel="preload" as="script"><link href="/revsuit/admin/js/chunk-vendors.js" rel="preload" as="script"><link href="/revsuit/admin/css/chunk-vendors.css" rel="stylesheet"><link href="/revsuit/admin/css/app.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but revsuit-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/revsuit/admin/js/chunk-vendors.js"></script><script src="/revsuit/admin/js/app.js"></script></body></html>
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+6
-6
File diff suppressed because one or more lines are too long
@@ -151,9 +151,6 @@ export default {
|
||||
if (localStorage.getItem("autoRefresh") === null) {
|
||||
this.autoRefresh = true;
|
||||
}
|
||||
if (this.autoRefresh && this.isLogMode) {
|
||||
this.timing()
|
||||
}
|
||||
this.GetVersion()
|
||||
},
|
||||
created() {
|
||||
@@ -177,8 +174,10 @@ export default {
|
||||
localStorage.setItem('autoRefresh', val)
|
||||
},
|
||||
isLogMode(val) {
|
||||
if (!val) {
|
||||
if (!val && this.timer) {
|
||||
clearInterval(this.timer)
|
||||
} else if (this.autoRefresh) {
|
||||
this.timing()
|
||||
}
|
||||
},
|
||||
refreshInterval(val) {
|
||||
@@ -248,7 +247,7 @@ html, body {
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
#app .trigger {
|
||||
|
||||
@@ -2,7 +2,7 @@ import axios from 'axios'
|
||||
import {store} from "@/main";
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: '/revsuit/api/', // api的base_url
|
||||
baseURL: location.pathname.slice(0, -'/admin/'.length) + "/api", // api的base_url
|
||||
timeout: 5000, // request timeout
|
||||
validateStatus: function (status) {
|
||||
if (status === 403) {
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
<a-select-option value='error'>ERROR</a-select-option>
|
||||
<a-select-option value='fatal'>FATAL</a-select-option>
|
||||
</a-select>
|
||||
<a-input :disabled="key ==='Addr' && parent==='Http'" v-else v-model="form[key]"/>
|
||||
<a-input :disabled="(key ==='Addr'||key==='AdminPathPrefix') && parent==='Http'" v-else
|
||||
v-model="form[key]"/>
|
||||
</a-form-model-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
@@ -12,13 +12,13 @@ module.exports = {
|
||||
'/api': {
|
||||
target: 'http://localhost:10000',
|
||||
pathRewrite: {
|
||||
'^/revsuit/api': '/revsuit/api'
|
||||
'^/api': '/revsuit/api'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
productionSourceMap:false,
|
||||
productionSourceMap: false,
|
||||
runtimeCompiler: true,
|
||||
filenameHashing: false,
|
||||
publicPath: ''
|
||||
publicPath: '/revsuit/admin/',
|
||||
}
|
||||
|
||||
@@ -15,17 +15,17 @@ require (
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.6 // indirect
|
||||
github.com/miekg/dns v1.1.38
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sinlov/qqwry-golang v0.0.0-20191204062238-bea9868bbbf4
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/ugorji/go v1.2.4 // indirect
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||
gorm.io/driver/sqlite v1.1.4
|
||||
gorm.io/gorm v1.21.4
|
||||
|
||||
@@ -427,6 +427,10 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@@ -518,8 +522,9 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc=
|
||||
@@ -668,6 +673,7 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -16,6 +16,7 @@ func Start() {
|
||||
log.ConsoleConfig{
|
||||
Level: log.LevelInfo,
|
||||
})
|
||||
defer log.Stop()
|
||||
app := &cli.App{
|
||||
Name: "RevSuit",
|
||||
Usage: "An Open-Sourced Reverse Platform Designed for Receive Various Kinds of Connection",
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
version: 1.1
|
||||
version: 1.3
|
||||
addr: :10000
|
||||
token:
|
||||
domain:
|
||||
external_ip: # You need to make sure that your target can access the server through this ip.
|
||||
admin_path_prefix: "/revsuit"
|
||||
database: revsuit.db
|
||||
log_level: info
|
||||
|
||||
ip_location_database:
|
||||
database: "qqwry" # qqwry or geoip.
|
||||
geo_license_key: "" # Mandatory field, if you choose to use GeoIP.
|
||||
|
||||
http:
|
||||
ip_header:
|
||||
dns:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package ipinfo
|
||||
|
||||
type Config struct {
|
||||
Database string
|
||||
GeoLicenseKey string `yaml:"geo_license_key"`
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ipinfo
|
||||
|
||||
type Database interface {
|
||||
Area(string) string
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
Url = "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz"
|
||||
)
|
||||
|
||||
func get(url string) (b []byte, err error) {
|
||||
var resp *http.Response
|
||||
client := http.Client{
|
||||
Timeout: 90 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // disable verify
|
||||
}}
|
||||
request, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
request.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36")
|
||||
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
log.Warn("%v", err)
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func extractTarGz(gzipStream io.Reader) error {
|
||||
uncompressedStream, err := gzip.NewReader(gzipStream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(uncompressedStream)
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeReg:
|
||||
if !strings.HasSuffix(header.Name, "GeoLite2-City.mmdb") {
|
||||
continue
|
||||
}
|
||||
outFile, err := os.Create("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||
_ = outFile.Close()
|
||||
return err
|
||||
}
|
||||
err = outFile.Close()
|
||||
if err != nil {
|
||||
log.Warn("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func download(licenseKey string) (err error) {
|
||||
var GeoLite2TarGz []byte
|
||||
log.Info("Downloading GeoLite2-City.mmdb...")
|
||||
if GeoLite2TarGz, err = get(fmt.Sprintf(Url, licenseKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return extractTarGz(bytes.NewReader(GeoLite2TarGz))
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
geo *geoip2.Reader
|
||||
}
|
||||
|
||||
var geo *geoip2.Reader
|
||||
var once sync.Once
|
||||
|
||||
// Area returns IpArea according to ip
|
||||
func (db *Database) Area(ip string) string {
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
record, err := db.geo.City(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
country := record.Country.Names["en"]
|
||||
city := record.City.Names["en"]
|
||||
if city == "" {
|
||||
city = record.Location.TimeZone
|
||||
}
|
||||
return fmt.Sprintf("%s %s", country, city)
|
||||
}
|
||||
|
||||
func checkUpdate(licenseKey string) {
|
||||
info, err := os.Stat("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err := download(licenseKey)
|
||||
if err != nil {
|
||||
log.Warn("Download GeoLite2-City.mmdb failed, caused by:%v, recommend to download it by yourself otherwise the `IpArea` will be null", err)
|
||||
}
|
||||
}
|
||||
} else if -time.Until(info.ModTime()) > 7*24*time.Hour {
|
||||
log.Info("Updating GeoLite2-City.mmdb...")
|
||||
err := download(licenseKey)
|
||||
if err != nil {
|
||||
log.Warn("Update GeoLite2-City.mmdb failed, please download GeoLite2-City.mmdb by yourself")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func New(licenseKey string) *Database {
|
||||
once.Do(func() {
|
||||
var err error
|
||||
checkUpdate(licenseKey)
|
||||
geo, err = geoip2.Open("GeoLite2-City.mmdb")
|
||||
if err != nil {
|
||||
log.Error("Load GeoLite2-City.mmdb failed, `IpArea` will be null")
|
||||
}
|
||||
})
|
||||
return &Database{geo: geo}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ipinfo
|
||||
|
||||
import (
|
||||
"github.com/li4n0/revsuit/internal/ipinfo/geoip"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo/qqwry"
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
var db Database
|
||||
|
||||
func Area(ip string) string {
|
||||
if db != nil {
|
||||
return db.Area(ip)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func Init(config Config) {
|
||||
switch config.Database {
|
||||
case "qqwry":
|
||||
db = qqwry.New()
|
||||
case "geoip":
|
||||
db = geoip.New(config.GeoLicenseKey)
|
||||
default:
|
||||
log.Fatal("wrong ip location database type: %q", config.Database)
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -16,10 +17,11 @@ import (
|
||||
|
||||
const (
|
||||
CopyWriteUrl = "https://qqwry.mirror.noc.one/copywrite.rar"
|
||||
QqwryUrl = "https://qqwry.mirror.noc.one/qqwry.rar"
|
||||
Url = "https://qqwry.mirror.noc.one/qqwry.rar"
|
||||
)
|
||||
|
||||
func get(url string) (b []byte, err error) {
|
||||
var resp *http.Response
|
||||
client := http.Client{
|
||||
Timeout: 90 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
@@ -28,14 +30,19 @@ func get(url string) (b []byte, err error) {
|
||||
request, _ := http.NewRequest("GET", url, nil)
|
||||
request.Header.Add("User-Agent", "Nali/2.1.2 (Nali CLI, https://nali.skk.moe)")
|
||||
|
||||
resp, err := client.Do(request)
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, err = ioutil.ReadAll(resp.Body)
|
||||
return b, err
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
log.Warn("%v", err)
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func getKey(b []byte) (key uint32, err error) {
|
||||
@@ -47,26 +54,36 @@ func getKey(b []byte) (key uint32, err error) {
|
||||
}
|
||||
|
||||
func decrypt(b []byte, key uint32) (_ []byte, err error) {
|
||||
var rc io.ReadCloser
|
||||
for i := 0; i < 0x200; i++ {
|
||||
key *= uint32(0x805)
|
||||
key++
|
||||
key &= uint32(0xff)
|
||||
b[i] = b[i] ^ byte(key)
|
||||
}
|
||||
rc, err := zlib.NewReader(bytes.NewBuffer(b))
|
||||
rc, err = zlib.NewReader(bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
return ioutil.ReadAll(rc)
|
||||
|
||||
defer func(rc io.ReadCloser) {
|
||||
err := rc.Close()
|
||||
if err != nil {
|
||||
log.Warn("%v", err)
|
||||
}
|
||||
}(rc)
|
||||
|
||||
return io.ReadAll(rc)
|
||||
}
|
||||
|
||||
func download() (err error) {
|
||||
var (
|
||||
copyWriteData, qqwryData []byte
|
||||
wg sync.WaitGroup
|
||||
key uint32
|
||||
)
|
||||
log.Info("Downloading qqwry.dat...")
|
||||
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -77,23 +94,21 @@ func download() (err error) {
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if qqwryData, err = get(QqwryUrl); err != nil {
|
||||
if qqwryData, err = get(Url); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var key uint32
|
||||
if key, err = getKey(copyWriteData); err != nil {
|
||||
return
|
||||
}
|
||||
b, err := decrypt(qqwryData, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = ioutil.WriteFile("qqwry.dat", b, 0644)
|
||||
|
||||
return nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if key, err = getKey(copyWriteData); err != nil {
|
||||
return err
|
||||
}
|
||||
if b, err := decrypt(qqwryData, key); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return os.WriteFile("qqwry.dat", b, 0644)
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,29 @@ import (
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
wry *qqwry.QQwry
|
||||
}
|
||||
|
||||
// Area returns IpArea according to ip
|
||||
func (db *Database) Area(ip string) string {
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
if db.wry == nil {
|
||||
return ""
|
||||
}
|
||||
ipData := db.wry.SearchByIPv4(ip)
|
||||
if ipData.Area == " CZ88.NET" {
|
||||
return ipData.Country
|
||||
}
|
||||
return ipData.Country + " " + ipData.Area
|
||||
}
|
||||
|
||||
var wry *qqwry.QQwry
|
||||
var once sync.Once
|
||||
|
||||
func init() {
|
||||
_ = log.NewConsole(100,
|
||||
log.ConsoleConfig{
|
||||
Level: log.LevelInfo,
|
||||
})
|
||||
func checkUpdate() {
|
||||
info, err := os.Stat("qqwry.dat")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -25,7 +40,7 @@ func init() {
|
||||
log.Warn("Download qqwry.dat failed, caused by:%v, recommend to download it by yourself otherwise the `IpArea` will be null", err)
|
||||
}
|
||||
}
|
||||
} else if -time.Until(info.ModTime()) > 5*24*time.Hour {
|
||||
} else if -time.Until(info.ModTime()) > 7*24*time.Hour {
|
||||
log.Info("Updating qqwry.dat...")
|
||||
err := download()
|
||||
if err != nil {
|
||||
@@ -34,8 +49,9 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func GetQQWry() *qqwry.QQwry {
|
||||
func New() *Database {
|
||||
once.Do(func() {
|
||||
checkUpdate()
|
||||
qqwry.DatData.FilePath = "qqwry.dat"
|
||||
init := qqwry.DatData.InitDatFile()
|
||||
if v, ok := init.(error); ok {
|
||||
@@ -46,6 +62,5 @@ func GetQQWry() *qqwry.QQwry {
|
||||
}
|
||||
wry = qqwry.NewQQwry()
|
||||
})
|
||||
|
||||
return wry
|
||||
return &Database{wry: wry}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package qqwry
|
||||
|
||||
// Area returns IpArea according to ip
|
||||
func Area(ip string) string {
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
if GetQQWry() == nil {
|
||||
return ""
|
||||
}
|
||||
ipData := GetQQWry().SearchByIPv4(ip)
|
||||
if ipData.Area == " CZ88.NET" {
|
||||
return ipData.Country
|
||||
}
|
||||
return ipData.Country + " " + ipData.Area
|
||||
}
|
||||
@@ -13,7 +13,7 @@ type Record interface {
|
||||
}
|
||||
|
||||
type BaseRecord struct {
|
||||
Record `gorm:"-" json:"-"`
|
||||
Record `gorm:"-" json:"-" notice:"-"`
|
||||
ID uint `gorm:"primarykey" form:"id" json:"id" notice:"-"`
|
||||
RuleName string `gorm:"index" form:"rule_name" json:"rule_name" notice:"rule"`
|
||||
Flag string `gorm:"index" form:"flag" json:"flag" `
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/internal/newdns"
|
||||
"github.com/li4n0/revsuit/internal/qqwry"
|
||||
"github.com/li4n0/revsuit/internal/recycler"
|
||||
"github.com/li4n0/revsuit/internal/rule"
|
||||
"github.com/patrickmn/go-cache"
|
||||
@@ -135,7 +135,7 @@ func (s *Server) newZone(name string) *newdns.Zone {
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := newRecord(_rule, flag, domain, ip, qqwry.Area(ip))
|
||||
r, err := newRecord(_rule, flag, domain, ip, ipinfo.Area(ip))
|
||||
if err != nil {
|
||||
log.Warn("DNS record(rule_id:%s) created failed :%s", _rule.Name, err)
|
||||
return nil, nil
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/file"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/internal/notice"
|
||||
"github.com/li4n0/revsuit/internal/qqwry"
|
||||
"github.com/li4n0/revsuit/internal/record"
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
@@ -137,7 +137,7 @@ func ListRecords(c *gin.Context) {
|
||||
|
||||
func createRecord(_rule *Rule, flag, flagGroup, user, password, method, path, filename, ip string, uploadData []byte, status Status) {
|
||||
// create new record
|
||||
area := qqwry.Area(ip)
|
||||
area := ipinfo.Area(ip)
|
||||
var ftpFile *file.FTPFile
|
||||
var r *Record
|
||||
var err error
|
||||
|
||||
+4
-3
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/file"
|
||||
"github.com/li4n0/revsuit/internal/qqwry"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/pkg/mysql/vmysql"
|
||||
"github.com/pkg/errors"
|
||||
log "unknwon.dev/clog/v2"
|
||||
@@ -130,7 +130,7 @@ func (s *Server) ConnectionClosed(c *vmysql.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
r, err := newRecord(_rule, flag, user, schema, clientName, clientOS, ip, qqwry.Area(ip), supportLoadLocalData, files)
|
||||
r, err := newRecord(_rule, flag, user, schema, clientName, clientOS, ip, ipinfo.Area(ip), supportLoadLocalData, files)
|
||||
if err != nil {
|
||||
log.Warn("MySQL record[rule_id: %s] created failed: %s", _rule.Name, err)
|
||||
return
|
||||
@@ -302,10 +302,11 @@ func (s *Server) Run() {
|
||||
s.listener, err = vmysql.NewListener("tcp", s.Addr, authServer, s, s.VersionString, 0, 0)
|
||||
if err != nil {
|
||||
log.Error("New MySQL Server failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
||||
s.livingLock.Lock()
|
||||
if !s.Enable {
|
||||
s.listener.Close()
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/qqwry"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
@@ -174,7 +174,7 @@ func (s *Server) Receive(c *gin.Context) {
|
||||
}
|
||||
|
||||
// create new record
|
||||
r, err := NewRecord(_rule, flag, c.Request.Method, u, ip, qqwry.Area(ip), string(raw))
|
||||
r, err := NewRecord(_rule, flag, c.Request.Method, u, ip, ipinfo.Area(ip), string(raw))
|
||||
if err != nil {
|
||||
log.Warn("HTTP record[rule_id:%d] created failed :%s", _rule.ID, err)
|
||||
code, err := strconv.Atoi(compileTpl(c, _rule.ResponseStatusCode, vars))
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ func ListRecords(c *gin.Context) {
|
||||
db.Where("method = ?", httpRecord.Method)
|
||||
}
|
||||
if httpRecord.URI != "" {
|
||||
db.Where("path like ?", "%"+httpRecord.URI+"%")
|
||||
db.Where("uri like ?", "%"+httpRecord.URI+"%")
|
||||
}
|
||||
if httpRecord.RemoteIP != "" {
|
||||
db.Where("remote_ip = ?", httpRecord.RemoteIP)
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/qqwry"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/internal/recycler"
|
||||
"github.com/pkg/errors"
|
||||
log "unknwon.dev/clog/v2"
|
||||
@@ -111,7 +111,7 @@ func (s *Server) handleConnection(conn net.Conn) {
|
||||
continue
|
||||
}
|
||||
|
||||
area := qqwry.Area(ip)
|
||||
area := ipinfo.Area(ip)
|
||||
|
||||
// create new record
|
||||
r, err := NewRecord(_rule, flag, path, ip, area)
|
||||
|
||||
+16
-13
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/pkg/dns"
|
||||
"github.com/li4n0/revsuit/pkg/ftp"
|
||||
"github.com/li4n0/revsuit/pkg/mysql"
|
||||
@@ -16,17 +17,19 @@ type noticeConfig struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Version float64
|
||||
Addr string
|
||||
Token string
|
||||
Domain string
|
||||
ExternalIP string `yaml:"external_ip"`
|
||||
Database string
|
||||
LogLevel string `yaml:"log_level"`
|
||||
Notice noticeConfig
|
||||
rhttp.Config
|
||||
DNS dns.Config
|
||||
MySQL mysql.Config
|
||||
RMI rmi.Config
|
||||
FTP ftp.Config
|
||||
Version float64
|
||||
Addr string
|
||||
Token string
|
||||
Domain string
|
||||
ExternalIP string `yaml:"external_ip"`
|
||||
AdminPathPrefix string `yaml:"admin_path_prefix"`
|
||||
Database string
|
||||
LogLevel string `yaml:"log_level"`
|
||||
IpLocationDatabase ipinfo.Config `yaml:"ip_location_database"`
|
||||
Notice noticeConfig
|
||||
HTTP rhttp.Config
|
||||
DNS dns.Config
|
||||
MySQL mysql.Config
|
||||
RMI rmi.Config
|
||||
FTP ftp.Config
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
func auth(c *gin.Context) {
|
||||
func (revsuit *Revsuit) auth(c *gin.Context) {
|
||||
c.SetSameSite(http.SameSiteLaxMode)
|
||||
c.SetCookie("token", c.Request.Header["Token"][0], 0, "/revsuit/api/", "", false, true)
|
||||
c.SetCookie("token", c.Request.Header["Token"][0], 0, revsuit.config.AdminPathPrefix, "", false, true)
|
||||
c.String(200, "pong")
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/li4n0/revsuit/frontend"
|
||||
@@ -28,7 +29,7 @@ func (revsuit *Revsuit) registerRouter() {
|
||||
|
||||
func (revsuit *Revsuit) registerPlatformRouter() {
|
||||
// /api need Authorization
|
||||
api := revsuit.http.Router.Group("/revsuit/api")
|
||||
api := revsuit.http.Router.Group(revsuit.config.AdminPathPrefix + "/api")
|
||||
api.Use(func(c *gin.Context) {
|
||||
cookieToken, err := c.Request.Cookie("token")
|
||||
if !(c.Request.Header.Get("Token") == revsuit.http.Token || err == nil && cookieToken.Value == revsuit.http.Token) {
|
||||
@@ -39,7 +40,7 @@ func (revsuit *Revsuit) registerPlatformRouter() {
|
||||
revsuit.http.ApiGroup = api
|
||||
|
||||
//platform routers
|
||||
api.GET("/auth", auth)
|
||||
api.GET("/auth", revsuit.auth)
|
||||
api.GET("/events", revsuit.events)
|
||||
api.GET("/ping", ping)
|
||||
api.GET("/version", version)
|
||||
@@ -53,7 +54,7 @@ func (revsuit *Revsuit) registerHttpRouter() {
|
||||
if err != nil {
|
||||
log.Fatal("Failed to sub path `dist`: %v", err)
|
||||
}
|
||||
revsuit.http.Router.StaticFS("/revsuit/admin", http.FS(fe))
|
||||
revsuit.http.Router.StaticFS(path.Clean(revsuit.config.AdminPathPrefix+"/admin"), http.FS(fe))
|
||||
|
||||
// init settings router group
|
||||
settingsGroup := revsuit.http.ApiGroup.Group("setting")
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/li4n0/revsuit/internal/database"
|
||||
"github.com/li4n0/revsuit/internal/file"
|
||||
"github.com/li4n0/revsuit/internal/ipinfo"
|
||||
"github.com/li4n0/revsuit/internal/notice"
|
||||
"github.com/li4n0/revsuit/internal/record"
|
||||
"github.com/li4n0/revsuit/pkg/dns"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
log "unknwon.dev/clog/v2"
|
||||
)
|
||||
|
||||
const VERSION = "0.1.4-beta"
|
||||
const VERSION = "0.1.7"
|
||||
|
||||
type Revsuit struct {
|
||||
config *Config
|
||||
@@ -178,6 +179,7 @@ func New(c *Config) *Revsuit {
|
||||
|
||||
initDatabase(c.Database)
|
||||
logLevel := initLog(c.LogLevel)
|
||||
ipinfo.Init(c.IpLocationDatabase)
|
||||
initNotice(c.Notice)
|
||||
|
||||
s := &Revsuit{
|
||||
@@ -204,8 +206,8 @@ func New(c *Config) *Revsuit {
|
||||
if c.Token != "" {
|
||||
s.http.SetToken(c.Token)
|
||||
}
|
||||
if c.IpHeader != "" {
|
||||
s.http.SetIpHeader(c.IpHeader)
|
||||
if c.HTTP.IpHeader != "" {
|
||||
s.http.SetIpHeader(c.HTTP.IpHeader)
|
||||
}
|
||||
if c.Domain != "" {
|
||||
s.dns.SetServerDomain(c.Domain)
|
||||
|
||||
@@ -153,10 +153,11 @@ func (revsuit *Revsuit) getPlatformConfig(c *gin.Context) {
|
||||
res["Addr"] = revsuit.config.Addr
|
||||
res["Token"] = revsuit.config.Token
|
||||
res["Domain"] = revsuit.config.Domain
|
||||
res["AdminPathPrefix"] = revsuit.config.AdminPathPrefix
|
||||
res["ExternalIP"] = revsuit.config.ExternalIP
|
||||
res["Database"] = revsuit.config.Database
|
||||
res["LogLevel"] = revsuit.config.LogLevel
|
||||
res["IpHeader"] = revsuit.config.IpHeader
|
||||
res["IpHeader"] = revsuit.config.HTTP.IpHeader
|
||||
|
||||
c.JSON(200, res)
|
||||
}
|
||||
@@ -194,8 +195,8 @@ func (revsuit *Revsuit) updatePlatformConfig(c *gin.Context) {
|
||||
log.Info("Update platform config [database] to %s", form["Database"])
|
||||
}
|
||||
|
||||
if form["IpHeader"] != revsuit.config.IpHeader {
|
||||
revsuit.config.IpHeader = form["IpHeader"]
|
||||
if form["IpHeader"] != revsuit.config.HTTP.IpHeader {
|
||||
revsuit.config.HTTP.IpHeader = form["IpHeader"]
|
||||
revsuit.http.SetIpHeader(form["IpHeader"])
|
||||
log.Info("Update http config [ip_header] to %s", form["IpHeader"])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user