Skip to content

Commit

Permalink
Feat - Better payload generator (#20)
Browse files Browse the repository at this point in the history
* WIP

* WIP

* WIP

* WIP

* WIP

* Removed testing config

* Fix - Migration scripts

* Fix - Migration script

* Fix - Bump dependencies

* Fix - include_schemas

* Fix - Migration script

* Switch to batch migrate/

* Added default

* WIP

* WIP

* WIP

* Fix sqlalchemy dialect

* WIP

* WIP

* WIP

* WIP

* Fix code color

* Migrate headers

* Fix data rendering

* Adjusted README
  • Loading branch information
daxAKAhackerman authored Mar 27, 2021
1 parent 8b8c9c2 commit b5969c6
Show file tree
Hide file tree
Showing 21 changed files with 694 additions and 566 deletions.
8 changes: 4 additions & 4 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ verify_ssl = true
name = "pypi"

[packages]
psycopg2-binary = "==2.8.5"
psycopg2-binary = "==2.8.6"
waitress = "==1.4.3"
Flask = "==1.1.1"
Flask = "==1.1.2"
Flask-Cors = "==3.0.10"
Flask-Migrate = "==2.5.2"
Flask-SQLAlchemy = "==2.4.1"
Flask-Migrate = "==2.7.0"
Flask-SQLAlchemy = "==2.4.4"
Flask-JWT-Extended = "==4.0.2"

[dev-packages]
Expand Down
216 changes: 113 additions & 103 deletions Pipfile.lock

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ XSS Catcher is a simple application that facilitates blind Cross-Site Scripting
- Separates the gathered data by clients
- Multi-user with administrative and low privilege users
- Stores information about the triggered XSS payloads like User-Agent, source IP address, timestamp, etc.
- Allows capture of cookies, local storage, session storage and any other specified parameters
- Payload can be customized by the users as he pleases. Simply pass your data in the query string or POST body and the application will catch it!
- Allows capture of cookies, local storage, session storage, and more.
- Acts as a "catch-all" endpoint. Just send your data in the querystring (GET) or body (POST) to your client's URL and XSS Catcher will catch it!
- Leverages [html2canvas](https://github.com/niklasvh/html2canvas) and [fingerprintjs](https://github.com/fingerprintjs/fingerprintjs)
- Captures the full DOM so you can easily know where the payload triggered
- Granular deletion of captured data
- Allows you to add custom tags to your XSS to better categorize them.

## Installation

Expand All @@ -57,6 +57,9 @@ $ make deploy
# Pull the repository
$ git pull

# Before running an update, it is recommended to make a copy of your database in case something unexpected happens
$ cp -r /var/lib/docker/volumes/xss-catcher_xss-db/ /var/lib/docker/volumes/xss-catcher_xss-db-bak/

# Update the application
$ make update
```
Expand Down
82 changes: 57 additions & 25 deletions client/public/scripts/collector.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/public/static/collector.min.js

Large diffs are not rendered by default.

246 changes: 111 additions & 135 deletions client/src/components/index/generatePayload/GetPayload.vue
Original file line number Diff line number Diff line change
@@ -1,90 +1,72 @@
<template>
<b-modal
size="md"
size="xl"
ref="getPayloadModal"
id="get-payload-modal"
title="Payload"
hide-footer
@hidden="cleanup()"
>
<b-form v-on:submit.prevent>
<b-form-group class="text-left">
<div v-if="xss_payload !== ''">
<b-form-group>
<b-form-textarea
rows="3"
no-auto-shrink
readonly
v-model="xss_payload"
></b-form-textarea>
<br />
<b-link
v-clipboard:copy="xss_payload"
@click="
makeToast('Payload copied to clipboard. ', 'success', 'OK')
"
>Copy to clipboard</b-link
>
</b-form-group>
<hr />
</div>
<b-form-group label="XSS type: ">
<b-form-radio-group
v-model="options.stored"
:options="options.typeList"
buttons
button-variant="outline-primary"
></b-form-radio-group>
</b-form-group>
<hr />
<b-form-group label="Gather data: ">
<b-form-checkbox-group
@change="options.gatherAll = []"
v-model="options.gatherData"
:options="options.gatherDataList"
buttons
button-variant="outline-primary"
></b-form-checkbox-group>
<br />
<br />
<b-form-checkbox-group
@change="options.gatherData = []"
v-model="options.gatherAll"
:options="options.gatherAllList"
buttons
button-variant="outline-primary"
></b-form-checkbox-group>
</b-form-group>
<hr />
<b-form-group label="Code type: ">
<b-form-radio-group
v-model="options.code_type"
:options="options.codeTypeList"
buttons
button-variant="outline-primary"
></b-form-radio-group>
</b-form-group>
<hr />
</b-form-group>

<b-form-group label="Other data: ">
<b-form-input
@keyup.enter="getPayload"
v-model="options.other"
name="input"
label="Other data: "
placeholder="param1=value1&param2=value2"
></b-form-input>
</b-form-group>
<div class="text-right">
<b-button @click="getPayload()" variant="outline-info"
>Generate</b-button
>
<b-button @click="cleanup()" variant="outline-secondary"
>Cancel</b-button
>
</div>
</b-form>
<div v-if="xss_payload !== ''">
<b-form-textarea
rows="3"
no-auto-shrink
readonly
v-model="xss_payload"
></b-form-textarea>
<br />
<b-link
v-clipboard:copy="xss_payload"
@click="makeToast('Payload copied to clipboard. ', 'success', 'OK')"
>Copy to clipboard</b-link
>
<hr />
</div>
<p>XSS type</p>
<b-form-radio-group
class="payload-double-selector"
v-model="xss_type"
:options="options.xss_type"
buttons
button-variant="outline-primary"
></b-form-radio-group>
<hr />
<p>Code type</p>
<b-form-radio-group
class="payload-double-selector"
v-model="code_type"
:options="options.code_type"
buttons
button-variant="outline-primary"
></b-form-radio-group>
<hr />
<p>Data to gather</p>
<b-form-checkbox-group
class="payload-single-selector"
@change="all = []"
v-model="to_gather"
:options="options.to_gather"
buttons
button-variant="outline-primary"
></b-form-checkbox-group>
<br />
<br />
<b-form-checkbox-group
class="payload-single-selector"
@change="to_gather = []"
v-model="all"
:options="options.all"
buttons
button-variant="outline-primary"
></b-form-checkbox-group>
<hr />
<p>Tags</p>
<b-form-tags tag-variant="info" v-model="tags"></b-form-tags>
<br />
<div class="text-right">
<b-button @click="getPayload()" variant="outline-info">Generate</b-button>
<b-button @click="cleanup()" variant="outline-secondary">Cancel</b-button>
</div>
</b-modal>
</template>

Expand All @@ -98,93 +80,75 @@ export default {
data() {
return {
options: {
gatherData: [],
gatherAll: [],
gatherDataList: [
to_gather: [
{ text: "Local storage", value: "local_storage" },
{ text: "Session storage", value: "session_storage" },
{ text: "Cookies", value: "cookies" },
{ text: "Origin URL", value: "geturl" },
{ text: "Origin URL", value: "origin_url" },
{ text: "Referrer", value: "referrer" },
{ text: "DOM", value: "dom" },
{ text: "Screenshot", value: "screenshot" },
{ text: "Fingerprint", value: "fingerprint" },
],
typeList: [
{ text: "Reflected", value: false },
{ text: "Stored", value: true },
xss_type: [
{ text: "Stored", value: "s" },
{ text: "Reflected", value: "r" },
],
codeTypeList: [
code_type: [
{ text: "HTML", value: "html" },
{ text: "JavaScript", value: "js" },
],
gatherAllList: [
{
text: "All of the above + screenshot/fingerprint/DOM",
value: "all",
},
],
stored: false,
code_type: "html",
other: "",
all: [{ text: "All of the above", value: "all" }],
},
to_gather: [],
all: [],
xss_type: "s",
code_type: "html",
xss_payload: "",
tags: [],
};
},
methods: {
getPayload() {
const path = `${basePath}/xss/generate`;
let payload = {
url: location.origin,
code: this.options.code_type,
code_type: this.code_type,
xss_type: this.xss_type,
client_id: this.client_id,
to_gather: this.to_gather,
tags: this.tags,
};
if (this.options.gatherAll.includes("all")) {
payload.i_want_it_all = 1;
}
if (this.options.gatherData.includes("cookies")) {
payload.cookies = 1;
}
if (this.options.gatherData.includes("local_storage")) {
payload.local_storage = 1;
}
if (this.options.gatherData.includes("session_storage")) {
payload.session_storage = 1;
}
if (this.options.stored) {
payload.stored = 1;
}
if (this.options.gatherData.includes("geturl")) {
payload.geturl = 1;
}
if (this.options.other) {
const otherDataList = this.options.other.split("&");
let otherDataDict = {};
for (const element of otherDataList) {
const element_splitted = element.split("=");
otherDataDict[element_splitted[0]] = element_splitted[1];
}
payload = Object.assign(payload, otherDataDict);
if (this.all.includes("all")) {
payload.to_gather = [
"local_storage",
"session_storage",
"cookies",
"origin_url",
"referrer",
"dom",
"screenshot",
"fingerprint",
];
}
axios
.get(path, { params: payload })
.post(path, payload)
.then((response) => {
this.xss_payload = response.data;
this.xss_payload = response.data.detail;
})
.catch((error) => {
this.handleError(error);
});
},
cleanup() {
this.to_gather = [];
this.all = [];
this.xss_type = "s";
this.code_type = "html";
this.xss_payload = "";
this.options.gatherData = [];
this.options.stored = false;
this.options.code_type = "html";
this.options.other = "";
this.tags = [];
this.$refs.getPayloadModal.hide();
this.$emit("get-clients");
Expand All @@ -194,6 +158,18 @@ export default {
</script>

<style>
.payload-single-selector {
width: 100%;
}
.payload-double-selector {
width: 100%;
}
.payload-double-selector label {
width: 50%;
}
.btn-outline-primary:not(:disabled):not(.disabled):active,
.btn-outline-primary:not(:disabled):not(.disabled).active,
.show > .btn-outline-primary.dropdown-toggle {
Expand Down
27 changes: 14 additions & 13 deletions client/src/components/index/shared/ViewDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
</b-td>
<b-td>{{ data.ip_addr }}</b-td>
</b-tr>
<b-tr>
<b-td valign="top">
<b>Tags:</b>
</b-td>
<b-td
><b-badge variant="info" v-for="tag in data.tags" :key="tag">{{
tag
}}</b-badge></b-td
>
</b-tr>
<b-tr>
<b-td valign="top">
<b>HTTP headers:</b>
Expand All @@ -29,11 +39,7 @@
<div v-highlight>
<pre
class="language-http"
><code><div v-for="(header_value, header_name) in data.headers" v-bind:key="header_name"><div
v-for="(header_value_deep, header_name_deep) in header_value"
v-bind:key="header_name_deep"
>{{ header_name_deep }}: {{ header_value_deep }}
</div></div></code></pre>
><code :key="componentKey"><div v-for="(header_value, header_name) in data.headers" v-bind:key="header_name">{{ header_name }}: {{ header_value }}</div></code></pre>
</div>
</b-td>
</b-tr>
Expand Down Expand Up @@ -116,13 +122,8 @@
"
>
<h4>{{ data_name }}</h4>
<div v-for="(value, param) in data_value" v-bind:key="param">
<div
v-for="(value_deep, param_deep) in value"
v-bind:key="param_deep"
>
<code>{{ param_deep }} => {{ value_deep }}</code>
</div>
<div v-for="(item, key) in data_value" v-bind:key="key">
<code>{{ key }} => {{ item }}</code>
</div>
<p></p>
</div>
Expand Down Expand Up @@ -187,7 +188,7 @@ export default {
});
},
getSpecificData(loot_type) {
const path = `${basePath}/xss/${this.xss_id}/data/${loot_type}`;
const path = `${basePath}/xss/${this.xss_id}/data/${loot_type}`;
axios
.get(path)
Expand Down
Loading

0 comments on commit b5969c6

Please sign in to comment.