본문 바로가기
picoCTF

No Sql Injection

웹 페이지는 다음과 같다. 이메일과 비밀번호로 로그인을 해야 한다. 이메일 칸에 내 Gmail로 로그인을 해봤는데 안 되었다.

 

 

문제에서 웹 페이지의 소스코드를 줬다. 소스코드에 로그인에 도움이 되는 힌트가 있지 않을까 싶어서 파일을 다운받은 뒤 압축을 풀었다. 

javascript 코드에서 로그인과 관련된 부분이 있을 것 같다. 

 

const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
const path = require("path");
const crypto = require("crypto");

const app = express();
const port = process.env.PORT | 3000;

// Middleware to parse JSON data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// User schema and model
const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  firstName: { type: String, required: true },
  lastName: { type: String, required: true },
  password: { type: String, required: true },
  token: { type: String, required: false, default: "{{Flag}}" },
});

const User = mongoose.model("User", userSchema);

// Initialize MongoMemoryServer and connect to it
async function startServer() {
  try {
    const mongoServer = await MongoMemoryServer.create();
    const mongoUri = mongoServer.getUri();
    await mongoose.connect(mongoUri);

    // Store initial user
    const initialUser = new User({
      firstName: "pico",
      lastName: "player",
      email: "picoplayer355@picoctf.org",
      password: crypto.randomBytes(16).toString("hex").slice(0, 16),
    });
    await initialUser.save();

    // Serve the HTML form
    app.get("/", (req, res) => {
      res.sendFile(path.join(__dirname, "index.html"));
    });

    // Serve the admin page
    app.get("/admin", (req, res) => {
      res.sendFile(path.join(__dirname, "admin.html"));
    });

    // Handle login form submission with JSON
    app.post("/login", async (req, res) => {
      const { email, password } = req.body;

      try {
        const user = await User.findOne({
          email:
            email.startsWith("{") && email.endsWith("}")
              ? JSON.parse(email)
              : email,
          password:
            password.startsWith("{") && password.endsWith("}")
              ? JSON.parse(password)
              : password,
        });

        if (user) {
          res.json({
            success: true,
            email: user.email,
            token: user.token,
            firstName: user.firstName,
            lastName: user.lastName,
          });
        } else {
          res.json({ success: false });
        }
      } catch (err) {
        res.status(500).json({ success: false, error: err.message });
      }
    });

    app.listen(port, () => {
    });
  } catch (err) {
    console.error(err);
  }
}

startServer().catch((err) => console.error(err));

 

이 부분을 보면 이메일은 picoplayer355@picoctf.org를 사용하고 password는 random으로 설정한다. 

 

  const initialUser = new User({
      firstName: "pico",
      lastName: "player",
      email: "picoplayer355@picoctf.org",
      password: crypto.randomBytes(16).toString("hex").slice(0, 16),
    });

 

1234를 password로 주고 로그인을 시도했는데 비밀번호가 틀렸다고 alert창을 띄운다.

 

 

어떻게 하면 로그인을 할 수 있을까 고민하다가 문제 제목에 대해서 생각해 보았다. 문제 제목이 NoSQL injection이니 이전과는 약간 다른 SQL injection을 하는 문제이지 않을까 싶다. 나는 SQL injection은 알지만 NoSQL injection은 모르기 때문에 구글에서 검색을 했다. 

 

Burp Suite에서 NoSQL injection에 관한 글이 있어서 해당 글을 읽어봤다. 

$ne 연산자를 이용하면 invalid 하지 않은 username, password의 query를 db에서 보여준다고 한다.

 

{"username":{"$ne":"invalid"},"password":{"$ne":"invalid"}}

 

우리의 경우 email은 이미 알고 있으니 pasword 부분에 아래의 payload를 입력하고 로그인을 시도했다. 

 

{"$ne":"invalid"}

 

그랬더니 로그인에 성공했다. 문제는 웹사이트가 정적인 웹 페이지여서 쿠키 값도 없고  flag에 관한 정보가 하나도 없어서 당황했다.

 

 

이럴 때는 개발자 도구의 network 탭을 켜고 다시 한번 로그인하는 것이 도움이 될 때가 종종 있다. 

 

나의 경우 brave browser를 쓰는데 개발자 도구의 Console에서 preserve log에 체크를 표시를 하자.

 

Network 탭에도 preserve log에 체크를 하자. 

 

 

 

Preserve log를 체크하지 않으면 웹 페이지에 로그인을 하면서 이루어지는 과정을 우리가 볼 수 없게 된다. 

 

다시 로그인을 했더니 어떤 정보를 가지고 로그인을 했는지 console 탭에서 보여준다. 

 

 

token값이 뒤에 '=='으로 끝나는 걸 보니 딱 봐도 token이 base64 인코딩이 되어있다. 

 

Base64 디코딩을 하면 picoCTF sql injection이라는 flag가 나온다. 

 

base64 -d <<<'cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uXzc4NGU0MGU4fQ=='
picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_784e40e8}

 

문제는 풀었으니 NoSQL이 뭔지 알아보자. NoSQL(Not Only SQL)은 기존의 relational database들과는 다른 database를 가리킨다. NoSQL은 SQL과 달리 table, row, columns를 사용하지 않고 JSON과 같은 key, value pair 등의 다양한 형태로 data를 저장한다. 

SQL은 ACID(Atomicity,Consistency,Isolation,Durability)등을 중요시하는 반면 NoSQL은 ACID보다는 속도와 유연성을 우선시한다.  NoSqL은 크게 Document-Oriented Database, Key-Value Stores, Column Family Stores, Graph Databases, Time-Series Databases, Object-Oriented Databases, Search-Engines로 나뉜다. 

 

SQL에 크게 관심이 없고 잘 모르는 나도 들어본 MongoDB와 CouchDB는 대표적인 Document-Oriented Database이고 Redis는 대표적인 Key-value stores이다.  마지막으로 NoSQL은 이름에 SQL이 들어가 있을 뿐인지 SQL이 아니다.

'picoCTF' 카테고리의 다른 글

Blast from the past  (1) 2024.11.10
Who are you?  (3) 2024.11.09
ASCII Numbers  (3) 2024.11.06
weirdSnake  (0) 2024.11.06
SansAlpha  (0) 2024.11.06