본문 바로가기
분류 전/CTF

[ShaktiCTF25] FRIENDS 풀이

by jwcs 2025. 7. 30.
728x90
반응형

개요

 

graphql을 다루는 문제다.

 

기능 분석

/

처음 접근하면 이런 페이지가 나온다. 아무런 상호작용할 부분이 없기에 첨부된 파일을 확인해보았다.

 

const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const {
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLID,
  GraphQLString,
  GraphQLList,
} = require("graphql");
const rateLimit = require("express-rate-limit");
const depthLimit = require("graphql-depth-limit");
const path = require("path");

const app = express();
const limiter = rateLimit({ windowMs: 60 * 1000, max: 10 });
app.use(limiter);
app.use(express.static(path.join(__dirname, "public")));

const users = [
  { id: "1", name: "Monica", friends: ["2", "3"] },  //other fields may exist
  { id: "2", name: "Rachel", friends: ["4"] },
  { id: "3", name: "Ross", friends: ["5"] },
  { id: "4", name: "Phoebe", friends: ["6"] },
  { id: "5", name: "Joey", friends: ["6"] },
  { id: "6", name: "Chandler", friends: [] }
];

const getUserById = (id) => users.find((u) => u.id === id);

const noIntrospectionRule = (context) => ({
  Field(node) {
    if (node.name.value === "__schema" || node.name.value === "__type") {
      throw new Error("Introspection is disabled");
    }
  },
});

const UserType = new GraphQLObjectType({
  name: "User",
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    friends: {
      type: GraphQLList(UserType),
      resolve: (user, args, context) => {
        context.traversedFromFriendChain = true;
        return user.friends.map(getUserById);
      },
    }
    // other fields may exist...
  }),
});

const QueryType = new GraphQLObjectType({
  name: "Query",
  fields: {
    me: { type: UserType, resolve: () => getUserById("1") },
    user: {
      type: UserType,
      args: { id: { type: GraphQLID } },
      resolve: (_, { id }) => getUserById(id),
    },
  },
});

const schema = new GraphQLSchema({ query: QueryType });

app.use(
  "/graphql",
  graphqlHTTP((req) => ({
    schema,
    graphiql: true,
    validationRules: [depthLimit(3), noIntrospectionRule],
    context: {},
  }))
);

app.listen(1337, () => console.log("App running on port 1337"));

/graphql 페이지가 있어 접근해주었다.

 

/graphql

그럼 위와 같은 graphql을 사용할 수 있는 페이지가 나온다.

 

사용법

{
  user(id:1){
    id
    name
    friends{
      id
    }
  }
}

위와 같이 사용하면 서버로부터 응답값을 받을 수 있다.

익스플로잇

const users = [
  { id: "1", name: "Monica", friends: ["2", "3"] },  //other fields may exist
  { id: "2", name: "Rachel", friends: ["4"] },
  { id: "3", name: "Ross", friends: ["5"] },
  { id: "4", name: "Phoebe", friends: ["6"] },
  { id: "5", name: "Joey", friends: ["6"] },
  { id: "6", name: "Chandler", friends: [] }
];

주석을 보면 표시되지 않은 다른 필드가 있다는 것을 알 수 있다.

 

const noIntrospectionRule = (context) => ({
  Field(node) {
    if (node.name.value === "__schema" || node.name.value === "__type") {
      throw new Error("Introspection is disabled");
    }
  },
});

하지만 introspection이 막혀있다. 따라서 다른 방안을 모색해야 한다.

 

실패

그냥 flag 필드가 있는지 확인해보면 flag 필드가 없기 때문에 에러가 반환되는 것을 알 수 있다.

 

description

문제 description에 secretFlag가 눈에 띈다. 카멜 케이스로 작성된 것이 필드에 쓰였을 것으로 보인다.

 

secretFlag 필드 발견

아까와 달리 secretFlag에 null이 적혀서 반환됐다. secretFlag라는 필드가 있다는 의미이다. 따라서 모든 user들을 순회하며 이 필드를 확인하면 될 것이다.

 

flag

6번째 user의 secretFlag 필드에 flag가 있는 것을 확인할 수 있다.

shaktictf{monica_doesnt_know_this_one}

 

대응 방안

  • introspection이 적절히 막혀 있다. 하지만 secretFlag 필드에 대한 조회를 방지하기 위해선 사용자로부터 입력값 제어를 제한하든지, secretFlag 필드에 대한 접근을 추가로 막아야할 것이다.

 

 

728x90
반응형

'분류 전 > CTF' 카테고리의 다른 글

[ShaktiCTF25] brain_games 풀이  (2) 2025.07.30
[ShaktiCTF25] Hooman 풀이  (3) 2025.07.30
[DownUnderCTF 2025] rocky 풀이  (0) 2025.07.22
[DownUnderCTF 2025] mini-me 풀이  (0) 2025.07.22
[L3ak CTF 2025] babyrev 풀이  (1) 2025.07.16