Author SHA1 Message Date
Li4n0 1144f67d37 feat: add domain and external ip configuration items (#18) 2021-05-22 20:40:04 +08:00
Li4n0 bc6e3962f8 fix: fix some bugs (#17) 2021-05-18 22:45:18 +08:00
66 changed files with 372 additions and 257 deletions
+1
View File
@@ -41,6 +41,7 @@ jobs:
CGO_ENABLE: 1 CGO_ENABLE: 1
run: | run: |
GOARCH="amd64" go build -v -ldflags="-s -w" -trimpath -o "bin/revsuit_darwin_amd64" ./cmd/revsuit/revsuit.go 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
- name: Upload the artifacts - name: Upload the artifacts
uses: li4n0/upload-release-action@v2 uses: li4n0/upload-release-action@v2
with: with:
+1 -1
View File
@@ -7,7 +7,7 @@ revsuit.db-shm
revsuit.db-wal revsuit.db-wal
# local config # local config
config.yaml config.yaml*
# binary # binary
revsuit revsuit
+1 -1
View File
@@ -1 +1 @@
.custom-icons-list[data-v-05a908c2] .anticon{margin-right:6px}body[data-v-6db702b8],html[data-v-6db702b8]{height:100%;margin:0}.fade-transform-enter-active[data-v-6db702b8],.fade-transform-leave-active[data-v-6db702b8]{transition:all .3s;opacity:0}.fade-transform-enter[data-v-6db702b8],.fade-transform-leave[data-v-6db702b8]{opacity:0}.fade-transform-enter-to[data-v-6db702b8]{opacity:0}.ant-menu-item>span>a[data-v-6db702b8]{color:hsla(0,0%,100%,.65)}.ant-menu-item-selected>span>a[data-v-6db702b8]{color:#fff}#nav[data-v-6db702b8]{height:100%}#nav .trigger[data-v-6db702b8]{font-size:18px;line-height:64px;padding:0 24px;cursor:pointer;transition:color .3s}#nav .trigger[data-v-6db702b8]:hover{color:#1890ff}#nav .logo[data-v-6db702b8]{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-6db702b8]{color:#888;text-align:right;margin-right:1rem} .custom-icons-list[data-v-05a908c2] .anticon{margin-right:6px}body[data-v-4ae4fda9],html[data-v-4ae4fda9]{height:100%;margin:0}.fade-transform-enter-active[data-v-4ae4fda9],.fade-transform-leave-active[data-v-4ae4fda9]{transition:all .3s;opacity:0}.fade-transform-enter[data-v-4ae4fda9],.fade-transform-leave[data-v-4ae4fda9]{opacity:0}.fade-transform-enter-to[data-v-4ae4fda9]{opacity:0}.ant-menu-item>span>a[data-v-4ae4fda9]{color:hsla(0,0%,100%,.65)}.ant-menu-item-selected>span>a[data-v-4ae4fda9]{color:#fff}#nav[data-v-4ae4fda9]{height:100%}#nav .trigger[data-v-4ae4fda9]{font-size:18px;line-height:64px;padding:0 24px;cursor:pointer;transition:color .3s}#nav .trigger[data-v-4ae4fda9]:hover{color:#1890ff}#nav .logo[data-v-4ae4fda9]{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-4ae4fda9]{color:#888;text-align:right;margin-right:1rem}
-1
View File
@@ -1 +0,0 @@
#add-rule[data-v-e8e0136c]{margin-bottom:10px}
-1
View File
@@ -1 +0,0 @@
#add-rule[data-v-2586daf9]{margin-bottom:10px}
-1
View File
@@ -1 +0,0 @@
#add-rule[data-v-487bb706]{margin-bottom:10px}
+1
View File
@@ -0,0 +1 @@
#add-rule[data-v-ae6501fc]{margin-bottom:10px}
+1
View File
@@ -0,0 +1 @@
#add-rule[data-v-5aa75eae]{margin-bottom:10px}
+1
View File
@@ -0,0 +1 @@
#add-rule[data-v-4bcc6b83]{margin-bottom:10px}
+1
View File
@@ -0,0 +1 @@
#add-rule[data-v-79ad429e]{margin-bottom:10px}.full-screen-icon[data-v-79ad429e]{position:absolute;z-index:5;color:#9e9e9e;right:1px;font-size:1rem;cursor:pointer;transition:font-size .1s}.full-screen-icon[data-v-79ad429e]:hover{font-size:1.1rem;transition:font-size .1s}
+1
View File
@@ -0,0 +1 @@
#add-rule[data-v-376729b6]{margin-bottom:10px}
-1
View File
@@ -1 +0,0 @@
#add-rule[data-v-169b853c]{margin-bottom:10px}.full-screen-icon[data-v-169b853c]{position:absolute;z-index:5;color:#9e9e9e;right:1px;font-size:1rem;cursor:pointer;transition:font-size .1s}.full-screen-icon[data-v-169b853c]:hover{font-size:1.1rem;transition:font-size .1s}
-1
View File
@@ -1 +0,0 @@
#add-rule[data-v-98f944a6]{margin-bottom:10px}
+1 -1
View File
@@ -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-012c9c90.css" rel="prefetch"><link href="css/chunk-0155152b.css" rel="prefetch"><link href="css/chunk-1da7f23b.css" rel="prefetch"><link href="css/chunk-2e835d68.css" rel="prefetch"><link href="css/chunk-330a7e95.css" rel="prefetch"><link href="css/chunk-3d13e56e.css" rel="prefetch"><link href="css/chunk-d4730ae2.css" rel="prefetch"><link href="css/chunk-e00d17cc.css" rel="prefetch"><link href="css/chunk-e039e75a.css" rel="prefetch"><link href="css/chunk-fef965c2.css" rel="prefetch"><link href="js/chunk-012c9c90.js" rel="prefetch"><link href="js/chunk-0155152b.js" rel="prefetch"><link href="js/chunk-08f9fbe8.js" rel="prefetch"><link href="js/chunk-1da7f23b.js" rel="prefetch"><link href="js/chunk-2e835d68.js" rel="prefetch"><link href="js/chunk-330a7e95.js" rel="prefetch"><link href="js/chunk-3d13e56e.js" rel="prefetch"><link href="js/chunk-b241c712.js" rel="prefetch"><link href="js/chunk-d4730ae2.js" rel="prefetch"><link href="js/chunk-e00d17cc.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="css/chunk-1da7f23b.css" rel="prefetch"><link href="css/chunk-330a7e95.css" rel="prefetch"><link href="css/chunk-36391b77.css" rel="prefetch"><link href="css/chunk-3d13e56e.css" rel="prefetch"><link href="css/chunk-42105840.css" rel="prefetch"><link href="css/chunk-65a35ecf.css" rel="prefetch"><link href="css/chunk-7bcb83e6.css" rel="prefetch"><link href="css/chunk-b8f538aa.css" rel="prefetch"><link href="css/chunk-e039e75a.css" rel="prefetch"><link href="css/chunk-fef965c2.css" rel="prefetch"><link href="js/chunk-08f9fbe8.js" rel="prefetch"><link href="js/chunk-1da7f23b.js" rel="prefetch"><link href="js/chunk-330a7e95.js" rel="prefetch"><link href="js/chunk-36391b77.js" rel="prefetch"><link href="js/chunk-3d13e56e.js" rel="prefetch"><link href="js/chunk-42105840.js" rel="prefetch"><link href="js/chunk-65a35ecf.js" rel="prefetch"><link href="js/chunk-7bcb83e6.js" rel="prefetch"><link href="js/chunk-b241c712.js" rel="prefetch"><link href="js/chunk-b8f538aa.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>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+19 -9
View File
@@ -2,8 +2,9 @@
<a-layout id="nav"> <a-layout id="nav">
<a-layout-sider v-model="collapsed" :trigger="null" collapsible> <a-layout-sider v-model="collapsed" :trigger="null" collapsible>
<div class="logo"><b>R</b><span v-if="!collapsed"><b>ev</b>Suit</span></div> <div class="logo"><b>R</b><span v-if="!collapsed"><b>ev</b>Suit</span></div>
<a-menu theme="dark" mode="inline" :selectedKeys="[this.$route.path]" :open-keys.sync='openKeys'> <a-menu theme="dark" mode="inline" :selectedKeys="[this.$route.path]" :openKeys.sync="openKeys">
<!-- <a-menu-item key="/">--> <!-- <a-menu-item key="
/">-->
<!-- <router-link to="/">--> <!-- <router-link to="/">-->
<!-- <a-icon type="dashboard"/>--> <!-- <a-icon type="dashboard"/>-->
<!-- <span>Dashboard</span>--> <!-- <span>Dashboard</span>-->
@@ -48,8 +49,7 @@
<a-menu-item key="/settings"> <a-menu-item key="/settings">
<router-link to="/settings"> <router-link to="/settings">
<a-icon type="setting"/> <a-icon type="setting"/>
Settings <span>Settings</span></router-link>
</router-link>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</a-layout-sider> </a-layout-sider>
@@ -60,7 +60,7 @@
:type="collapsed ? 'menu-unfold' : 'menu-fold'" :type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="() => (collapsed = !collapsed)" @click="() => (collapsed = !collapsed)"
/> />
<div v-if="isLogMode" style="float: right; min-width:50% ;padding: 12px 0;line-height: 24px;"> <div v-if="isLogMode" style="float: right; min-width:60% ;padding: 12px 0;line-height: 24px;">
<a-row :gutter="24" type="flex"> <a-row :gutter="24" type="flex">
<a-col :span="21"> <a-col :span="21">
<a-form-model v-show="showSettings" ref="settings" layout="inline"> <a-form-model v-show="showSettings" ref="settings" layout="inline">
@@ -123,7 +123,7 @@ export default {
pageSize: store.pageSize, pageSize: store.pageSize,
collapsed: false, collapsed: false,
showSettings: false, showSettings: false,
openKeys: ['logs'], openKeys: [],
version: "", version: "",
}; };
}, },
@@ -153,6 +153,14 @@ export default {
} }
this.GetVersion() this.GetVersion()
}, },
created() {
let unwatch = this.$watch('$route', function (to, from) {
if (from.path === "/" && this.openKeys.length === 0) {
this.openKeys.push(to.path.split("/")[1])
unwatch()
}
})
},
destroyed() { destroyed() {
clearInterval(this.timer) clearInterval(this.timer)
}, },
@@ -171,7 +179,6 @@ export default {
} else { } else {
this.timing() this.timing()
} }
localStorage.setItem('autoRefresh', val)
}, },
refreshInterval(val) { refreshInterval(val) {
clearInterval(this.timer) clearInterval(this.timer)
@@ -185,8 +192,11 @@ export default {
store.pageSize = val store.pageSize = val
localStorage.setItem('pageSize', val) localStorage.setItem('pageSize', val)
}, },
'store.authed'() { 'store.authed'(val) {
this.$refs.content.fetch() if (val) {
this.$refs.content.fetch()
this.GetVersion()
}
}, },
'store.pageSize'() { 'store.pageSize'() {
this.$refs.content.pagination.pageSize = store.pageSize this.$refs.content.pagination.pageSize = store.pageSize
-6
View File
@@ -5,9 +5,6 @@ export function auth(token) {
url: '/auth', url: '/auth',
method: 'get', method: 'get',
headers: {"Token": token}, headers: {"Token": token},
validateStatus: function (status) {
return status >= 200 && status < 300
}
}) })
} }
@@ -15,8 +12,5 @@ export function getVersion() {
return request({ return request({
url: '/version', url: '/version',
method: 'get', method: 'get',
validateStatus: function (status) {
return status >= 200 && status < 300
}
}) })
} }
+4
View File
@@ -1,9 +1,13 @@
import axios from 'axios' import axios from 'axios'
import {store} from "@/main";
const service = axios.create({ const service = axios.create({
baseURL: '/revsuit/api/', // api的base_url baseURL: '/revsuit/api/', // api的base_url
timeout: 5000, // request timeout timeout: 5000, // request timeout
validateStatus: function (status) { validateStatus: function (status) {
if (status === 403) {
store.authed = false
}
return status >= 200 && status < 300 // 默认的 return status >= 200 && status < 300 // 默认的
} }
}) })
+4 -4
View File
@@ -1,14 +1,14 @@
import request from './index' import request from './index'
export function getHttpConfig() { export function getPlatformConfig() {
return request({ return request({
url: '/setting/getHttpConfig', url: '/setting/getPlatformConfig',
}) })
} }
export function updateHttpConfig(data) { export function updatePlatformConfig(data) {
return request({ return request({
url: '/setting/updateHttpConfig', url: '/setting/updatePlatformConfig',
method: "post", method: "post",
data: data data: data
}) })
+5 -5
View File
@@ -15,20 +15,20 @@
<a-form-model-item :rules="rules.flagFormat" prop="flag_format"> <a-form-model-item :rules="rules.flagFormat" prop="flag_format">
<span slot="label"> <span slot="label">
Flag Format&nbsp; Flag Format&nbsp;
<a-tooltip title="Basic usage: <a-tooltip :title="`Basic usage:
1. Only when the request contains content that satisfies the flag format, the request will be captured. 1. Only when the request's ${flagField} contains content that satisfies the flag format, the request will be captured.
2. Use regular expression syntax. 2. Use regular expression syntax.
3. The character '*' means to capture all requests. 3. The character '*' means to capture all requests.
Advanced usage: Advanced usage:
1. When the regex uses grouping without group name, the platform will only notify the user or push to the client when the first group appears for the first time. 1. When the regex uses grouping without group name, the platform will only notify the user or push to the client when the first group appears for the first time.
2. When the regex uses grouping with group name, you can get these submatches through template variables and use them in other fields of the rule."> 2. When the regex uses grouping with group name, you can get these submatches through template variables and use them in other fields of the rule.`">
<a-icon type="question-circle-o"/> <a-icon type="question-circle-o"/>
</a-tooltip> </a-tooltip>
</span> </span>
<a-input <a-input
v-model="form.flag_format" v-model="form.flag_format"
style="width: 100%" style="width: 100%"
placeholder="please enter flag format" placeholder="Please enter flag format"
:readOnly="readOnly" :readOnly="readOnly"
/> />
</a-form-model-item> </a-form-model-item>
@@ -97,7 +97,7 @@ export default {
}, },
} }
}, },
props: ['form', 'readOnly'] props: ['form', 'readOnly', 'flagField']
} }
</script> </script>
+8 -6
View File
@@ -139,12 +139,14 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false message: 'Unknown error: ' + e.response.status,
return [] style: {
} else { width: '100px',
console.error(e) marginLeft: `${335 - 600}px`,
} },
duration: 4
});
}) })
}, },
}, },
+8 -6
View File
@@ -228,12 +228,14 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false message: 'Unknown error: ' + e.response.status,
return [] style: {
} else { width: '100px',
console.error(e) marginLeft: `${335 - 600}px`,
} },
duration: 4
});
}) })
}, },
}, },
+8 -6
View File
@@ -173,12 +173,14 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false message: 'Unknown error: ' + e.response.status,
return [] style: {
} else { width: '100px',
console.error(e) marginLeft: `${335 - 600}px`,
} },
duration: 4
});
}) })
}, },
}, },
+8 -6
View File
@@ -216,12 +216,14 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false message: 'Unknown error: ' + e.response.status,
return [] style: {
} else { width: '100px',
console.error(e) marginLeft: `${335 - 600}px`,
} },
duration: 4
});
}) })
}, },
}, },
+8 -6
View File
@@ -140,12 +140,14 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false message: 'Unknown error: ' + e.response.status,
return [] style: {
} else { width: '100px',
console.error(e) marginLeft: `${335 - 600}px`,
} },
duration: 4
});
}) })
}, },
}, },
+1 -6
View File
@@ -332,11 +332,7 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false
return []
} else {
this.$notification.error({
message: 'Unknown error: ' + e.response.status, message: 'Unknown error: ' + e.response.status,
style: { style: {
width: '100px', width: '100px',
@@ -344,7 +340,6 @@ export default {
}, },
duration: 4 duration: 4
}); });
}
}) })
}, },
clickSwitch(record, prop) { clickSwitch(record, prop) {
+2 -7
View File
@@ -28,7 +28,7 @@
<a-input <a-input
v-model="form.pasv_address" v-model="form.pasv_address"
style="width: 100%" style="width: 100%"
placeholder="use default value in the config" placeholder="Use external IP by default"
:readOnly="formReadOnly" :readOnly="formReadOnly"
/> />
</a-form-model-item> </a-form-model-item>
@@ -267,11 +267,7 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false
return []
} else {
this.$notification.error({
message: 'Unknown error: ' + e.response.status, message: 'Unknown error: ' + e.response.status,
style: { style: {
width: '100px', width: '100px',
@@ -279,7 +275,6 @@ export default {
}, },
duration: 4 duration: 4
}); });
}
}) })
}, },
clickSwitch(record, prop) { clickSwitch(record, prop) {
+1 -6
View File
@@ -374,11 +374,7 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false
return []
} else {
this.$notification.error({
message: 'Unknown error: ' + e.response.status, message: 'Unknown error: ' + e.response.status,
style: { style: {
width: '100px', width: '100px',
@@ -386,7 +382,6 @@ export default {
}, },
duration: 4 duration: 4
}); });
}
}) })
}, },
clickSwitch(record, prop) { clickSwitch(record, prop) {
+12 -11
View File
@@ -13,7 +13,7 @@
@close="closeDrawer" @close="closeDrawer"
> >
<a-form-model :model="form" ref="form" layout="vertical" @submit="handleSubmit"> <a-form-model :model="form" ref="form" layout="vertical" @submit="handleSubmit">
<BasicRule :form="form" :readOnly="formReadOnly"/> <BasicRule :form="form" :readOnly="formReadOnly" :flagField="'user or schema'"/>
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="24"> <a-col :span="24">
<a-form-model-item label="Files" :rules="rules.files"> <a-form-model-item label="Files" :rules="rules.files">
@@ -31,7 +31,8 @@
<a-form-model-item> <a-form-model-item>
<div class="ant-form-item-label"> <div class="ant-form-item-label">
<label for="exploit-jdbc-client">Exploit Jdbc Client <label for="exploit-jdbc-client">Exploit Jdbc Client
<a-tooltip placement="topLeft" title="Whether test to exploit jdbc client."> <a-tooltip placement="topLeft"
title="Whether test to exploit jdbc client.">
<a-icon type="question-circle"/> <a-icon type="question-circle"/>
</a-tooltip> </a-tooltip>
</label> </label>
@@ -41,8 +42,13 @@
</a-row> </a-row>
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="24"> <a-col :span="24">
<a-form-model-item label="Payload"> <a-form-model-item>
<a-input-group compact v-for="payloadKey in payloadKeys" :key="payloadKey"> <label for="payload">Payload
<a-tooltip placement="topLeft" title="Need to set ${payload} variable by flag format first. Payload which key is same as ${payload} will be used.">
<a-icon type="question-circle"/>
</a-tooltip>
</label>
<a-input-group id="payload" compact v-for="payloadKey in payloadKeys" :key="payloadKey">
<a-input v-model="form['Key-'+payloadKey]" <a-input v-model="form['Key-'+payloadKey]"
style="width: 47%;margin-bottom: 5px" style="width: 47%;margin-bottom: 5px"
v-decorator="['Key-'+payloadKey,]" v-decorator="['Key-'+payloadKey,]"
@@ -256,7 +262,7 @@ export default {
onShowSizeChange: (current, size) => { onShowSizeChange: (current, size) => {
store.pageSize = size store.pageSize = size
} }
}, filters: {}, }, filters: {},
loading: false, loading: false,
columns, columns,
form: {}, form: {},
@@ -291,11 +297,7 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false
return []
} else {
this.$notification.error({
message: 'Unknown error: ' + e.response.status, message: 'Unknown error: ' + e.response.status,
style: { style: {
width: '100px', width: '100px',
@@ -303,7 +305,6 @@ export default {
}, },
duration: 4 duration: 4
}); });
}
}) })
}, },
clickSwitch(record, prop) { clickSwitch(record, prop) {
+1 -6
View File
@@ -225,11 +225,7 @@ export default {
this.pagination = pagination; this.pagination = pagination;
this.loading = false this.loading = false
}).catch(e => { }).catch(e => {
if (e.response.status === 403) { this.$notification.error({
store.authed = false
return []
} else {
this.$notification.error({
message: 'Unknown error: ' + e.response.status, message: 'Unknown error: ' + e.response.status,
style: { style: {
width: '100px', width: '100px',
@@ -237,7 +233,6 @@ export default {
}, },
duration: 4 duration: 4
}); });
}
}) })
}, },
clickSwitch(record, prop) { clickSwitch(record, prop) {
+4 -4
View File
@@ -20,7 +20,7 @@
</template> </template>
<script> <script>
import {getHttpConfig, updateHttpConfig} from "@/api/settings" import {getPlatformConfig, updatePlatformConfig} from "@/api/settings"
import SettingForm from "@/components/SettingForm"; import SettingForm from "@/components/SettingForm";
export default { export default {
@@ -37,7 +37,7 @@ export default {
}, },
methods: { methods: {
getConfig() { getConfig() {
getHttpConfig().then(res => { getPlatformConfig().then(res => {
this.form = res.data this.form = res.data
}).catch(e => { }).catch(e => {
this.$notification.error({ this.$notification.error({
@@ -55,10 +55,10 @@ export default {
updateConfig() { updateConfig() {
this.spinning = true this.spinning = true
let targetConfig = JSON.stringify(this.form) let targetConfig = JSON.stringify(this.form)
updateHttpConfig(this.form).then( updatePlatformConfig(this.form).then(
() => { () => {
setTimeout(() => { setTimeout(() => {
getHttpConfig().then((res) => { getPlatformConfig().then((res) => {
this.spinning = false this.spinning = false
let nowConfig = JSON.stringify(res.data) let nowConfig = JSON.stringify(res.data)
if (nowConfig !== targetConfig) { if (nowConfig !== targetConfig) {
+12 -8
View File
@@ -5,13 +5,13 @@
:active-tab-key="tabKey" :active-tab-key="tabKey"
@tabChange="key => this.tabKey = key" @tabChange="key => this.tabKey = key"
style="width: 100%;"> style="width: 100%;">
<http v-if="tabKey==='HTTP'"></http> <http ref="settings" v-if="tabKey==='HTTP'"></http>
<dns v-else-if="tabKey==='DNS'"></dns> <dns ref="settings" v-else-if="tabKey==='DNS'"></dns>
<rmi v-else-if="tabKey==='RMI'"></rmi> <rmi ref="settings" v-else-if="tabKey==='RMI'"></rmi>
<mysql v-else-if="tabKey==='MYSQL'"></mysql> <mysql ref="settings" v-else-if="tabKey==='MYSQL'"></mysql>
<ftp v-else-if="tabKey==='FTP'"></ftp> <ftp ref="settings" v-else-if="tabKey==='FTP'"></ftp>
<notice v-else-if="tabKey==='NOTICE'"></notice> <notice ref="settings" v-else-if="tabKey==='NOTICE'"></notice>
<rules v-else-if="tabKey==='RULES'"></rules> <rules ref="settings" v-else-if="tabKey==='RULES'"></rules>
</a-card> </a-card>
</div> </div>
</template> </template>
@@ -42,7 +42,11 @@ export default {
} }
} }
, ,
methods: {}, methods: {
fetch() {
this.$refs.settings.getConfig();
}
},
components: { components: {
Http, Http,
Dns, Dns,
+2 -1
View File
@@ -7,4 +7,5 @@ const banner = `
/ _, _/ __/ |/ /___/ / /_/ / / /_ / _, _/ __/ |/ /___/ / /_/ / / /_
/_/ |_|\___/|___//____/\__,_/_/\__/ /_/ |_|\___/|___//____/\__,_/_/\__/
v%s v%s
https://revsuit.pro` https://revsuit.pro
`
+12 -1
View File
@@ -47,10 +47,21 @@ func Start() {
configFile = c.Path("config") configFile = c.Path("config")
} }
conf := &server.Config{} conf := &server.Config{}
tpl := &server.Config{}
_ = yaml.Unmarshal(configTemplate, tpl)
if content, err := os.ReadFile(configFile); err == nil { if content, err := os.ReadFile(configFile); err == nil {
if err := yaml.Unmarshal(content, conf); err != nil { if err := yaml.Unmarshal(content, conf); err != nil {
return err return err
} }
if conf.Version != tpl.Version {
backup := fmt.Sprintf("%s_%.1f", configFile, conf.Version)
log.Warn("Old version of configuration file detected, new configuration file being generated, old configuration backed up to %s", backup)
if err := os.WriteFile(backup, content, 0644); err != nil {
return err
}
return os.WriteFile(configFile, configTemplate, 0644)
}
} else if os.IsNotExist(err) && configFile == "config.yaml" { } else if os.IsNotExist(err) && configFile == "config.yaml" {
log.Warn("Generate default configurations to config.yaml, please configure and run again.") log.Warn("Generate default configurations to config.yaml, please configure and run again.")
return os.WriteFile("config.yaml", configTemplate, 0644) return os.WriteFile("config.yaml", configTemplate, 0644)
@@ -69,13 +80,13 @@ func Start() {
if c.String("log") != "" { if c.String("log") != "" {
conf.Database = c.String("log") conf.Database = c.String("log")
} }
fmt.Printf(banner, server.VERSION)
server.New(conf).Run() server.New(conf).Run()
return nil return nil
}, },
} }
sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands)) sort.Sort(cli.CommandsByName(app.Commands))
fmt.Printf(banner, server.VERSION)
err := app.Run(os.Args) err := app.Run(os.Args)
if err != nil { if err != nil {
+3 -2
View File
@@ -1,6 +1,8 @@
version: 4.0 version: 1.0
addr: :10000 addr: :10000
token: token:
domain:
external_ip: # You need to make sure that your target can access the server through this ip.
database: revsuit.db database: revsuit.db
log_level: info log_level: info
@@ -18,7 +20,6 @@ mysql:
ftp: ftp:
enable: true enable: true
addr: :21 addr: :21
pasv_ip: 127.0.0.1 # your public network ip
pasv_port: 2020 pasv_port: 2020
notice: notice:
dingtalk: https://oapi.dingtalk.com/robot/send?access_token={token} dingtalk: https://oapi.dingtalk.com/robot/send?access_token={token}
+1 -1
View File
@@ -25,7 +25,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) 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()) > 5*24*time.Hour {
log.Info("Updating qqwry.dat...") log.Info("Updating qqwry.dat...")
err := download() err := download()
if err != nil { if err != nil {
+78 -31
View File
@@ -17,9 +17,11 @@ import (
type Server struct { type Server struct {
Config Config
rules []*Rule serverDomain string
rulesLock sync.RWMutex serverIP string
livingLock sync.Mutex rules []*Rule
rulesLock sync.RWMutex
livingLock sync.Mutex
} }
var ( var (
@@ -35,6 +37,16 @@ func GetServer() *Server {
return server return server
} }
func (s *Server) SetServerDomain(serverDomain string) *Server {
s.serverDomain = serverDomain
return s
}
func (s *Server) SetServerIP(serverIP string) *Server {
s.serverIP = serverIP
return s
}
func (s *Server) getRules() []*Rule { func (s *Server) getRules() []*Rule {
defer s.rulesLock.RUnlock() defer s.rulesLock.RUnlock()
s.rulesLock.RLock() s.rulesLock.RLock()
@@ -53,6 +65,7 @@ func newSet(_rule *Rule, name, value, ip string, _type newdns.Type) []newdns.Set
{ {
Name: name, Name: name,
Type: _type, Type: _type,
TTL: _rule.TTL * time.Second,
Records: func() []newdns.Record { Records: func() []newdns.Record {
switch _rule.Type { switch _rule.Type {
case newdns.TXT: case newdns.TXT:
@@ -82,12 +95,20 @@ func newSet(_rule *Rule, name, value, ip string, _type newdns.Type) []newdns.Set
return []newdns.Record{{Address: value}} return []newdns.Record{{Address: value}}
} }
}(), }(),
TTL: _rule.TTL * time.Second,
}, },
} }
return set return set
} }
func getZoneName(domain string) string {
frags := strings.Split(domain, ".")
zoneName := domain
if len(frags) >= 2 {
zoneName = strings.Join(frags[len(frags)-2:], ".") + "."
}
return zoneName
}
// newZone creates new dns zone with root domain // newZone creates new dns zone with root domain
func (s *Server) newZone(name string) *newdns.Zone { func (s *Server) newZone(name string) *newdns.Zone {
defer func() { defer func() {
@@ -97,13 +118,8 @@ func (s *Server) newZone(name string) *newdns.Zone {
}() }()
domain := strings.TrimSuffix(name, ".") domain := strings.TrimSuffix(name, ".")
frags := strings.Split(domain, ".")
zoneName := name
if len(frags) >= 2 {
zoneName = strings.Join(frags[len(frags)-2:], ".") + "."
}
zone := &newdns.Zone{ zone := &newdns.Zone{
Name: zoneName, Name: getZoneName(domain),
MasterNameServer: "ns1.hostmaster.com.", MasterNameServer: "ns1.hostmaster.com.",
AllNameServers: []string{ AllNameServers: []string{
"ns1.hostmaster.com.", "ns1.hostmaster.com.",
@@ -126,29 +142,32 @@ func (s *Server) newZone(name string) *newdns.Zone {
} }
log.Info("DNS record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip) log.Info("DNS record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip)
//only send to client when this connection recorded first time. //only send to client or notify user when this connection recorded first time.
if _rule.PushToClient { var count int64
if flagGroup != "" { if flagGroup != "" {
var count int64 database.DB.Where("rule_name=? and domain like ?", _rule.Name, "%"+flagGroup+"%").Model(&Record{}).Count(&count)
database.DB.Where("rule_name=? and domain like ?", _rule.Name, "%"+flagGroup+"%").Model(&Record{}).Count(&count) }
if count <= 1 { if count <= 1 {
r.PushToClient() if _rule.PushToClient {
log.Trace("DNS record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
}
} else {
r.PushToClient() r.PushToClient()
log.Trace("DNS record[id:%d, flag:%s] has been put to client message queue", r.ID, flag) if flagGroup != "" {
log.Trace("DNS record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
} else {
log.Trace("DNS record[id:%d] has been put to client message queue", r.ID)
}
}
//send notice
if _rule.Notice {
go func() {
r.Notice()
if flagGroup != "" {
log.Trace("DNS record[id:%d, flagGroup:%s] notice has been sent", r.ID, flagGroup)
} else {
log.Trace("DNS record[id:%d] notice has been sent", r.ID)
}
}()
} }
} }
//send notice
if _rule.Notice {
go func() {
r.Notice()
log.Trace("DNS record[id:%d] notice has been sent", r.ID)
}()
}
if _rule.Value != "" { if _rule.Value != "" {
value := rule.CompileTpl(_rule.Value, vars) value := rule.CompileTpl(_rule.Value, vars)
_type := _rule.Type _type := _rule.Type
@@ -172,6 +191,12 @@ func (s *Server) Stop() {
s.livingLock.Unlock() s.livingLock.Unlock()
} }
func (s *Server) Restart() {
s.Stop()
time.Sleep(time.Second * 2)
go s.Run()
}
func (s *Server) Run() { func (s *Server) Run() {
s.Enable = true s.Enable = true
s.livingLock.Lock() s.livingLock.Lock()
@@ -192,12 +217,34 @@ func (s *Server) Run() {
// create server // create server
server := newdns.NewServer(newdns.Config{ server := newdns.NewServer(newdns.Config{
Handler: func(name string) (*newdns.Zone, error) { Handler: func(name string) (*newdns.Zone, error) {
if name == s.serverDomain+"." {
return &newdns.Zone{
Name: getZoneName(s.serverDomain),
MasterNameServer: "ns1.hostmaster.com.",
AllNameServers: []string{
"ns1.hostmaster.com.",
"ns2.hostmaster.com.",
"ns3.hostmaster.com.",
},
Handler: func(_, remoteAddr string) ([]newdns.Set, error) {
return []newdns.Set{
{
Name: name,
Type: newdns.A,
TTL: 10,
Records: []newdns.Record{
{
Address: s.serverIP,
},
}}}, nil
}}, nil
}
return s.newZone(name), nil return s.newZone(name), nil
}, },
}) })
// run server // run server
log.Info("Starting DNS Server at :53") log.Info("Starting DNS Server at :53, resolve %s to %s", s.serverDomain, s.serverIP)
go func() { go func() {
s.livingLock.Lock() s.livingLock.Lock()
if !s.Enable { if !s.Enable {
+1 -1
View File
@@ -13,7 +13,7 @@ import (
) )
type Rule struct { type Rule struct {
rule.BaseRule `yaml:""` rule.BaseRule `yaml:",inline"`
Type newdns.Type `gorm:"default:1" form:"type" json:"type"` Type newdns.Type `gorm:"default:1" form:"type" json:"type"`
Value string `form:"value" json:"value"` Value string `form:"value" json:"value"`
TTL time.Duration `gorm:"ttl;default:10" form:"ttl" json:"ttl"` TTL time.Duration `gorm:"ttl;default:10" form:"ttl" json:"ttl"`
+1 -2
View File
@@ -3,6 +3,5 @@ package ftp
type Config struct { type Config struct {
Enable bool Enable bool
Addr string Addr string
PasvIP string `yaml:"pasv_ip"` PasvPort int `yaml:"pasv_port"`
PasvPort int `yaml:"pasv_port"`
} }
+9 -3
View File
@@ -22,6 +22,7 @@ import (
type Server struct { type Server struct {
Config Config
pasvIP string
rules []*Rule rules []*Rule
rulesLock sync.RWMutex rulesLock sync.RWMutex
livingLock sync.Mutex livingLock sync.Mutex
@@ -55,6 +56,11 @@ func GetServer() *Server {
return server return server
} }
func (s *Server) SetPasvIP(ip string) *Server {
s.pasvIP = ip
return s
}
func (s *Server) getRules() []*Rule { func (s *Server) getRules() []*Rule {
defer s.rulesLock.RUnlock() defer s.rulesLock.RUnlock()
s.rulesLock.RLock() s.rulesLock.RLock()
@@ -194,10 +200,10 @@ loop:
_, _ = connBuf.WriteString(UserLogged) _, _ = connBuf.WriteString(UserLogged)
if pasvAddress = s.getPasvAddressFromCache(ip, _rule.PasvAddress); pasvAddress == "" { if pasvAddress = s.getPasvAddressFromCache(ip, _rule.PasvAddress); pasvAddress == "" {
pasvAddress = fmt.Sprintf("%s:%d", s.PasvIP, s.PasvPort) pasvAddress = fmt.Sprintf("%s:%d", s.pasvIP, s.PasvPort)
} }
isRedirect = rule.CompileTpl(pasvAddress, vars) != fmt.Sprintf("%s:%d", s.PasvIP, s.PasvPort) isRedirect = rule.CompileTpl(pasvAddress, vars) != fmt.Sprintf("%s:%d", s.pasvIP, s.PasvPort)
case "SIZE": case "SIZE":
path += strings.TrimLeft(args, "/") path += strings.TrimLeft(args, "/")
if _rule == nil || isRedirect || len(_rule.Data) == 0 { if _rule == nil || isRedirect || len(_rule.Data) == 0 {
@@ -312,7 +318,7 @@ func (s *Server) handlePasvConnection(conn net.Conn, data map[string]interface{}
// run pasv server // run pasv server
func (s *Server) runPasvServer() (net.Listener, error) { func (s *Server) runPasvServer() (net.Listener, error) {
pasvAddress := fmt.Sprintf("%s:%d", strings.Split(s.Addr, ":")[0], s.PasvPort) pasvAddress := fmt.Sprintf("%s:%d", strings.Split(s.Addr, ":")[0], s.PasvPort)
log.Info("Start to listen FTP PASV port at %v, PasvIP is %v", pasvAddress, s.PasvIP) log.Info("Start to listen FTP PASV port at %v, PasvIP is %v", pasvAddress, s.pasvIP)
listener, err := net.Listen("tcp", pasvAddress) listener, err := net.Listen("tcp", pasvAddress)
if err != nil { if err != nil {
+23 -19
View File
@@ -156,26 +156,30 @@ func createRecord(_rule *Rule, flag, flagGroup, user, password, method, path, fi
} }
log.Info("FTP record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip) log.Info("FTP record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip)
//only send to client when this connection recorded first time. //only send to client or notify user when this connection recorded first time.
if _rule.PushToClient { var count int64
if flagGroup != "" { if flagGroup != "" {
var count int64 database.DB.Where("rule_name=? and (user like ? or password like ?)", _rule.Name, "%"+flagGroup+"%", "%"+flagGroup+"%").Model(&Record{}).Count(&count)
database.DB.Where("rule_name=? and (user like ? or password like ?)", _rule.Name, "%"+flagGroup+"%", "%"+flagGroup+"%").Model(&Record{}).Count(&count) }
if count <= 1 { if count <= 1 {
r.PushToClient() if _rule.PushToClient {
log.Trace("FTP record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
}
} else {
r.PushToClient() r.PushToClient()
log.Trace("FTP record[id:%d, flag:%s] has been put to client message queue", r.ID, flag) if flagGroup != "" {
log.Trace("FTP record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
} else {
log.Trace("FTP record[id:%d] has been put to client message queue", r.ID)
}
}
//send notice
if _rule.Notice {
go func() {
r.Notice()
if flagGroup != "" {
log.Trace("FTP record[id:%d, flagGroup:%s] notice has been sent", r.ID, flagGroup)
} else {
log.Trace("FTP record[id:%d] notice has been sent", r.ID)
}
}()
} }
} }
//send notice
if _rule.Notice {
go func() {
r.Notice()
log.Trace("FTP record[id:%d] notice has been sent", r.ID)
}()
}
} }
+33 -34
View File
@@ -68,11 +68,10 @@ func (s *Server) NewConnection(c *vmysql.Conn) {
user = c.User user = c.User
schema = c.SchemaName schema = c.SchemaName
validated bool validated bool
flag string
) )
for _, _rule := range s.getRules() { for _, _rule := range s.getRules() {
flag, _, _ = _rule.Match(user) flag, flagGroup, vars := _rule.Match(user)
if flag == "" { if flag == "" {
flag, _, _ = _rule.Match(schema) flag, _, _ = _rule.Match(schema)
} }
@@ -82,6 +81,9 @@ func (s *Server) NewConnection(c *vmysql.Conn) {
log.Trace("MySQL connection[id: %d] matched rule[rule_name: %s, flag: %s]", c.ConnectionID, _rule.Name, flag) log.Trace("MySQL connection[id: %d] matched rule[rule_name: %s, flag: %s]", c.ConnectionID, _rule.Name, flag)
s.connRulePool.Store(c.ConnectionID, _rule) s.connRulePool.Store(c.ConnectionID, _rule)
validated = true validated = true
c.Flag = flag
c.FlagGroup = flagGroup
c.Vars = vars
break break
} }
if !validated { if !validated {
@@ -103,7 +105,6 @@ func (s *Server) ConnectionClosed(c *vmysql.Conn) {
var clientName, clientOS, flag, flagGroup string var clientName, clientOS, flag, flagGroup string
user := c.User user := c.User
schema := c.SchemaName
supportLoadLocalData := c.SupportLoadDataLocal supportLoadLocalData := c.SupportLoadDataLocal
cr, ok := s.connRulePool.Load(c.ConnectionID) cr, ok := s.connRulePool.Load(c.ConnectionID)
@@ -113,13 +114,6 @@ func (s *Server) ConnectionClosed(c *vmysql.Conn) {
} }
_rule := cr.(*Rule) _rule := cr.(*Rule)
// flag must not be empty
for _, s := range []string{user, schema} {
flag, flagGroup, _ = _rule.Match(s)
if flag != "" {
break
}
}
if c.ConnAttrs != nil { if c.ConnAttrs != nil {
clientName = c.ConnAttrs["_client_name"] + " " + c.ConnAttrs["_client_version"] clientName = c.ConnAttrs["_client_name"] + " " + c.ConnAttrs["_client_version"]
@@ -143,27 +137,31 @@ func (s *Server) ConnectionClosed(c *vmysql.Conn) {
} }
log.Info("MySQL record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip) log.Info("MySQL record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip)
//only send to client when this connection recorded first time. //only send to client or notify user when this connection recorded first time.
if _rule.PushToClient { var count int64
if flagGroup != "" { if c.FlagGroup != "" {
var count int64 database.DB.Where("rule_name=? and (user like ? or schema like ?)", _rule.Name, "%"+flagGroup+"%", "%"+flagGroup+"%").Model(&Record{}).Count(&count)
database.DB.Where("rule_name=? and (user like ? or schema like ?)", _rule.Name, "%"+flagGroup+"%", "%"+flagGroup+"%").Model(&Record{}).Count(&count)
if count <= 1 {
r.PushToClient()
log.Trace("MySQL record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
}
} else {
r.PushToClient()
log.Trace("MySQL record[id:%d, flag:%s] has been put to client message queue", r.ID, flag)
}
} }
if count <= 1 {
//send notice if _rule.PushToClient {
if _rule.Notice { r.PushToClient()
go func() { if flagGroup != "" {
r.Notice() log.Trace("MySQL record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
log.Trace("MySQL record[id: %d] notice has been sent", r.ID) } else {
}() log.Trace("MySQL record[id:%d] has been put to client message queue", r.ID)
}
}
//send notice
if _rule.Notice {
go func() {
r.Notice()
if flagGroup != "" {
log.Trace("MySQL record[id:%d, flagGroup:%s] notice has been sent", r.ID, flagGroup)
} else {
log.Trace("MySQL record[id: %d] notice has been sent", r.ID)
}
}()
}
} }
s.connRulePool.Delete(c.ConnectionID) s.connRulePool.Delete(c.ConnectionID)
@@ -203,12 +201,13 @@ func (s *Server) ComQuery(c *vmysql.Conn, query string, callback func(*sqltypes.
})} })}
//choose payload //choose payload
//jdbc:mysql://127.0.0.1:3306/test?connectionAttributes=t:cc7&autoDeserialize=true if c.Vars["payload"] != "" && _rule.Payloads[c.Vars["payload"]] != "" {
if c.ConnAttrs["t"] != "" && _rule.Payloads[c.ConnAttrs["t"]] != "" { payload, _ = base64.StdEncoding.DecodeString(_rule.Payloads[c.Vars["payload"]])
payload, _ = base64.StdEncoding.DecodeString(_rule.Payloads[c.ConnAttrs["t"]]) log.Trace("MySQL exploit client [%d] with payload [%s]", c.ConnectionID, c.Vars["payload"])
} else { } else {
for _, v := range _rule.Payloads { for k, v := range _rule.Payloads {
payload, _ = base64.StdEncoding.DecodeString(v) payload, _ = base64.StdEncoding.DecodeString(v)
log.Trace("MySQL not found payload variable, exploit client [%d] with payload [%s]", c.ConnectionID, k)
break break
} }
} }
+4
View File
@@ -152,6 +152,10 @@ type Conn struct {
ConnAttrs map[string]string ConnAttrs map[string]string
Files map[string][]byte Files map[string][]byte
Flag string
FlagGroup string
Vars map[string]string
} }
// bufPool is used to allocate and free buffers in an efficient way. // bufPool is used to allocate and free buffers in an efficient way.
+24 -20
View File
@@ -191,27 +191,31 @@ func (s *Server) Receive(c *gin.Context) {
} }
log.Info("HTTP record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip) log.Info("HTTP record[id:%d rule:%s remote_ip:%s] has been created", r.ID, _rule.Name, ip)
//only send to client when this connection recorded first time. //only send to client or notify user when this connection recorded first time.
if _rule.PushToClient { var count int64
if flagGroup != "" { if flagGroup != "" {
var count int64 database.DB.Where("rule_name=? and raw_request like ?", _rule.Name, "%"+flagGroup+"%").Model(&Record{}).Count(&count)
database.DB.Where("rule_name=? and raw_request like ?", _rule.Name, "%"+flagGroup+"%").Model(&Record{}).Count(&count)
if count <= 1 {
r.PushToClient()
log.Trace("HTTP record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
}
} else {
r.PushToClient()
log.Trace("HTTP record[id:%d, flag:%s] has been put to client message queue", r.ID, r.Flag)
}
} }
if count <= 1 {
//send notice if _rule.PushToClient {
if _rule.Notice { r.PushToClient()
go func() { if flagGroup != "" {
r.Notice() log.Trace("HTTP record[id:%d, flagGroup:%s] has been put to client message queue", r.ID, flagGroup)
log.Trace("HTTP record[id:%d] notice has been sent", r.ID) } else {
}() log.Trace("HTTP record[id:%d] has been put to client message queue", r.ID)
}
}
//send notice
if _rule.Notice {
go func() {
r.Notice()
if flagGroup != "" {
log.Trace("HTTP record[id:%d, flagGroup:%s] notice has been sent", r.ID, flagGroup)
} else {
log.Trace("HTTP record[id:%d] notice has been sent", r.ID)
}
}()
}
} }
for header, value := range _rule.ResponseHeaders { for header, value := range _rule.ResponseHeaders {
+8 -5
View File
@@ -16,11 +16,14 @@ type noticeConfig struct {
} }
type Config struct { type Config struct {
Addr string Version float64
Token string Addr string
Database string Token string
LogLevel string `yaml:"log_level"` Domain string
Notice noticeConfig ExternalIP string `yaml:"external_ip"`
Database string
LogLevel string `yaml:"log_level"`
Notice noticeConfig
rhttp.Config rhttp.Config
DNS dns.Config DNS dns.Config
MySQL mysql.Config MySQL mysql.Config
+1 -1
View File
@@ -18,7 +18,7 @@ import (
func auth(c *gin.Context) { func auth(c *gin.Context) {
c.SetSameSite(http.SameSiteLaxMode) c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("token", c.Request.Header["Token"][0], 0, "/revsuit/api/", c.Request.Host, true, true) c.SetCookie("token", c.Request.Header["Token"][0], 0, "/revsuit/api/", "", false, true)
c.String(200, "pong") c.String(200, "pong")
} }
+2 -2
View File
@@ -59,8 +59,8 @@ func (revsuit *Revsuit) registerHttpRouter() {
settingsGroup := revsuit.http.ApiGroup.Group("setting") settingsGroup := revsuit.http.ApiGroup.Group("setting")
settingsGroup.GET("/exportRules", exportRules) settingsGroup.GET("/exportRules", exportRules)
settingsGroup.POST("/importRules", importRules) settingsGroup.POST("/importRules", importRules)
settingsGroup.GET("/getHttpConfig", revsuit.getHttpConfig) settingsGroup.GET("/getPlatformConfig", revsuit.getPlatformConfig)
settingsGroup.POST("/updateHttpConfig", revsuit.updateHttpConfig) settingsGroup.POST("/updatePlatformConfig", revsuit.updatePlatformConfig)
settingsGroup.GET("/getDnsConfig", revsuit.getDnsConfig) settingsGroup.GET("/getDnsConfig", revsuit.getDnsConfig)
settingsGroup.POST("/updateDnsConfig", revsuit.updateDnsConfig) settingsGroup.POST("/updateDnsConfig", revsuit.updateDnsConfig)
settingsGroup.GET("/getRmiConfig", revsuit.getRmiConfig) settingsGroup.GET("/getRmiConfig", revsuit.getRmiConfig)
+9 -2
View File
@@ -14,7 +14,7 @@ import (
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
) )
const VERSION = "Beta0.1" const VERSION = "0.1.2-beta"
type Revsuit struct { type Revsuit struct {
config *Config config *Config
@@ -97,7 +97,7 @@ func initLog(level string) (logLevel log.Level) {
database.DB.Logger.LogMode(logger.Info) database.DB.Logger.LogMode(logger.Info)
logLevel = log.LevelTrace logLevel = log.LevelTrace
case "info": case "info":
gin.SetMode(gin.DebugMode) gin.SetMode(gin.ReleaseMode)
database.DB.Logger.LogMode(logger.Info) database.DB.Logger.LogMode(logger.Info)
logLevel = log.LevelInfo logLevel = log.LevelInfo
case "warning", "warn": case "warning", "warn":
@@ -181,6 +181,13 @@ func New(c *Config) *Revsuit {
if c.IpHeader != "" { if c.IpHeader != "" {
s.http.SetIpHeader(c.IpHeader) s.http.SetIpHeader(c.IpHeader)
} }
if c.Domain != "" {
s.dns.SetServerDomain(c.Domain)
}
if c.ExternalIP != "" {
s.dns.SetServerIP(c.ExternalIP)
s.ftp.SetPasvIP(c.ExternalIP)
}
return s return s
} }
+32 -7
View File
@@ -138,10 +138,12 @@ func importRules(c *gin.Context) {
}) })
} }
func (revsuit *Revsuit) getHttpConfig(c *gin.Context) { func (revsuit *Revsuit) getPlatformConfig(c *gin.Context) {
var res = make(map[string]string) var res = make(map[string]string)
res["Addr"] = revsuit.config.Addr res["Addr"] = revsuit.config.Addr
res["Token"] = revsuit.config.Token res["Token"] = revsuit.config.Token
res["Domain"] = revsuit.config.Domain
res["ExternalIP"] = revsuit.config.ExternalIP
res["Database"] = revsuit.config.Database res["Database"] = revsuit.config.Database
res["LogLevel"] = revsuit.config.LogLevel res["LogLevel"] = revsuit.config.LogLevel
res["IpHeader"] = revsuit.config.IpHeader res["IpHeader"] = revsuit.config.IpHeader
@@ -149,7 +151,7 @@ func (revsuit *Revsuit) getHttpConfig(c *gin.Context) {
c.JSON(200, res) c.JSON(200, res)
} }
func (revsuit *Revsuit) updateHttpConfig(c *gin.Context) { func (revsuit *Revsuit) updatePlatformConfig(c *gin.Context) {
var form = make(map[string]string) var form = make(map[string]string)
if err := c.ShouldBindJSON(&form); err != nil { if err := c.ShouldBindJSON(&form); err != nil {
@@ -188,6 +190,34 @@ func (revsuit *Revsuit) updateHttpConfig(c *gin.Context) {
log.Info("Update http config [ip_header] to %s", form["IpHeader"]) log.Info("Update http config [ip_header] to %s", form["IpHeader"])
} }
if form["Domain"] != revsuit.config.Domain {
revsuit.config.Domain = form["Domain"]
revsuit.dns.SetServerDomain(form["Domain"])
log.Info("Update platform config [domain] to %s", form["Domain"])
if revsuit.dns.Enable {
revsuit.dns.Restart()
}
}
if form["ExternalIP"] != revsuit.config.ExternalIP {
revsuit.config.ExternalIP = form["ExternalIP"]
revsuit.dns.SetServerIP(form["ExternalIP"])
revsuit.ftp.SetPasvIP(form["ExternalIP"])
log.Info("Update platform config [ExternalIP] to %s", form["ExternalIP"])
if revsuit.dns.Enable {
revsuit.dns.Restart()
}
if revsuit.ftp.Enable {
revsuit.ftp.Restart()
}
}
if form["Token"] != revsuit.config.Token {
revsuit.config.Token = form["Token"]
revsuit.http.SetToken(form["Token"])
log.Info("Update platform config [token] to %s", form["Token"])
}
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": "succeed", "status": "succeed",
"error": nil, "error": nil,
@@ -216,11 +246,6 @@ func (revsuit *Revsuit) updateFtpConfig(c *gin.Context) {
log.Info("Update ftp config [addr] to %s", form.Addr) log.Info("Update ftp config [addr] to %s", form.Addr)
} }
if form.PasvIP != revsuit.ftp.PasvIP {
revsuit.ftp.PasvIP = form.PasvIP
log.Info("Update ftp config [pasv_ip] to %s", form.PasvIP)
}
if form.PasvPort != revsuit.ftp.PasvPort { if form.PasvPort != revsuit.ftp.PasvPort {
revsuit.ftp.PasvPort = form.PasvPort revsuit.ftp.PasvPort = form.PasvPort
log.Info("Update ftp config [pasv_port] to %d", form.PasvPort) log.Info("Update ftp config [pasv_port] to %d", form.PasvPort)