feat: add peanutbutter challenges

This commit is contained in:
Abel Stuker 2024-11-25 22:31:56 +01:00
parent 08897d82e3
commit 26039148eb
36 changed files with 9652 additions and 0 deletions

View File

@ -0,0 +1,8 @@
# peanutbutter brand
## Text
Welcome to the Peanut Butter Blog.
Try to find the best Peanut Butter Brand.
## Files
site url
## How to Deploy
docker compose

View File

@ -0,0 +1,21 @@
## Difficulty
Easy
## Category
Web
## How To Solve
This time, simply modifying the URL doesn't help.
When you push the 'retrieve' button on the /profile page, you get a message telling you that you can only get the best brand when you like peanut butter. A quick look at the Chrome DevTools Network tab shows that this information is the result of a GET request to the server after the button click. Under the request header, you can see that a cookie named `token` is sent along. This is interesting. ![](devtools_brand_request.png)
When visiting the /profile page, a GET request for profile information is made to the server. The server responds with the information but also requires the browser to set a cookie. That cookie is a JSON Web Token (JWT) with the name `token` in this case. You can use the Chrome DevTools to inspect the cookie. ![](devtools_cookies.png) You can see that the cookie has the value `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQ1RGIFBhcnRpY2lwYW50IiwibGlrZXNfcGVhbnV0X2J1dHRlciI6ZmFsc2V9._DG-nLXVTzNw_BoSQ240P6QNL9JbxRz6aWAgPFiXfVU`. You can simple decode this JWT, using an [online tool](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQ1RGIFBhcnRpY2lwYW50IiwibGlrZXNfcGVhbnV0X2J1dHRlciI6ZmFsc2V9._DG-nLXVTzNw_BoSQ240P6QNL9JbxRz6aWAgPFiXfVU).
The obtained JSON is:
```json
{
"role": "CTF Participant",
"likes_peanut_butter": false
}
```
In this tool, modify the JSON by setting `likes_peanut_butter` to `true` (this was the requirement for obtaining the 'best brand'). You now get a new JWT: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQ1RGIFBhcnRpY2lwYW50IiwibGlrZXNfcGVhbnV0X2J1dHRlciI6dHJ1ZX0.Ndo_jZn8fFltuKiZK9lyVoXyLuiueaPLUmuC7_0Y8j8`.
In you Chrome DevTools, you can change the `token` cookie to the new JWT. Now, simply push the 'retrieve' button again to obtain the flag. Note that you should not refresh the page. That would result in a new GET request to obtain profile information and override the flag again to the old flag.
## Flag
`IGCTF{H3L4ES_PIND4K44SSSSSS}`

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

View File

@ -0,0 +1,7 @@
# peanutbutter extra secret post
## Text
Welcome to the Peanut Butter Blog. The post with id `ebddb7db-b20a-4e8f-b9e9-954e1c07ab83` contains the flag. I hope you have access.
## Files
site url
## How to Deploy
docker compose

View File

@ -0,0 +1,25 @@
## Difficulty
??
## Category
Web
## How To Solve
When you try to access the page `/posts/ebddb7db-b20a-4e8f-b9e9-954e1c07ab83`, you see that you have no access to the blog post. The network tab in the Chrome DevTools shows that a GET request is made to the server that responds with an array of blog posts. This is the same request as is made on the homepage to show the list of blog posts (except for the hidden one). The fact that all the blog posts are also fetched on this page is quite strange. In fact, before showing any blog post content, the client first fetches all blog posts and checks whether the visited blog post id is contained in the fetched blog posts list.
When visiting other blog posts, another request is made to the server after the blog post id was succesfully matched with the list of previously fetched blog posts. This other GET request obtaines the actual blog post content (`<server>/posts/<blogpost-id>`). Notice however that the server doesn't check the id on this this request, as it (stupidly) assumes that this already happened on the client. Simply executing this request using the given id should be sufficient to obtain the flag.
You can for example do this by right clicking the request, occuring in other blog posts, in the network tab of the DevTools. Then select copy as cURL, replace the other blog post id with the given blog post id, and perform the request, in your terminal for example.
```bash
> curl 'http://localhost:3000/posts/ebddb7db-b20a-4e8f-b9e9-954e1c07ab83' \
-H 'Accept: */*' \
-H 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,nl;q=0.7' \
-H 'Cache-Control: no-cache' \
-H 'Connection: keep-alive' \
-H 'DNT: 1' \
-H 'Origin: <ORIGIN>' \
-H 'Pragma: no-cache' \
-H 'Referer: <REFERER>'
{"name":"The other, even more secret secret of peanut butter","id":"ebddb7db-b20a-4e8f-b9e9-954e1c07ab83","show":false,"list":false,"content":"IGCTF{Pe4nut_Butt3R_1s_n0t_made_froM_butTEr}"}%
```
## Flag
`IGCTF{Pe4nut_Butt3R_1s_n0t_made_froM_butTEr}`

View File

@ -0,0 +1,7 @@
# peanutbutter fruit
## Text
Welcome to the Peanut Butter Blog. As you can see, only admins can see the superior fruit that goes extremely well with peanut butter.
## Files
site url
## How to Deploy
docker compose

View File

@ -0,0 +1,8 @@
## Difficulty
Easy
## Category
Web
## How To Solve
As you can see, only admins have access to the superior fruit. When you navigated from the home page to the profile page, you can see that the URL contains the word 'guest'. Simply replace that with the word 'admin' and voila.
## Flag
`IGCTF{Re4lly_any_fru1t_exc3pt_f0r_Mang03s}`

View File

@ -0,0 +1,8 @@
# peanutbutter secret post
## Text
Welcome to the Peanut Butter Blog.
The website contains some information that the developer tried to hide. He failed.
## Files
site url
## How to Deploy
docker compose

View File

@ -0,0 +1,10 @@
## Difficulty
??
## Category
Web
## How To Solve
When visiting the homepage, a GET request is made to the server to obtain the blog posts. Using the Chrome DevTools, you can inspect the response. ![](devtools_response.png)
Notice that this response corresponds to the blog posts visible on the homepage, with one difference. The homepage doesn't show the blog post with id `bee229bc-4147-4c34-9418-9572e9f0ee1b`. When clicking on other blog posts, you see that the URL for every blog post is structured as follows: `/posts/<id>`. Simply going to `/posts/bee229bc-4147-4c34-9418-9572e9f0ee1b`reveals the hidden blog post with the flag.
## Flag
`IGCTF{M4d3_fr0m_p3Anuts}`

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

View File

@ -0,0 +1,7 @@
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm i
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

View File

@ -0,0 +1,31 @@
import express from 'express';
import postsRouter from './routers/posts';
import profileRouter from './routers/profile';
import cors from 'cors';
import cookieParser from 'cookie-parser';
const app = express();
const port = 8000;
app.use(cookieParser());
const getBrand = (req, res) => {
const token = req.cookies.token;
if (!token) {
return res.status(401).json({ message: 'hmm, something went wrong' });
}
const parsed = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
if (parsed.likes_peanut_butter) {
res.json({ brand: 'IGCTF{H3L4ES_PIND4K44SSSSSS}' });
} else {
res.json({ brand: 'you only get the best brand when you actually like peanut butter' });
}
}
app.get('/brand', getBrand);
app.use("/profile", profileRouter);
app.use("/posts", postsRouter);
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "app.ts",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "npx tsx app.ts",
"start": "npx tsx app.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/express": "^5.0.0",
"eslint": "^9.13.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.3",
"tsx": "^4.19.1",
"typescript": "^5.6.3",
"typescript-eslint": "^8.11.0"
},
"dependencies": {
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^4.21.1"
}
}

View File

@ -0,0 +1,198 @@
import { Router } from 'express';
const router = Router();
const posts = [
{
name: "10 Surprising Uses for Peanut Butter That Dont Involve Eating It",
id: '0ae1ad6b-92c8-4902-905f-67987dcc47e5',
show: true,
content: `Peanut butter is a miracle spread in the eyes of its fans, but did you know its talents go way beyond the kitchen? Were talking about peanut butter in ways youd never expect: as an emergency household item, a craft supply, even a stress-relief tool. Lets dive into ten surprising ways you can put your peanut butter to work (though, fair warning, the last one might lead to some sticky chaos).\n
It turns out that peanut butter has a hidden talent for transforming scuffed-up shoes. Yes, a dollop of creamy peanut butter and a soft cloth can actually work wonders on leather shoes. After all, that natural oil and the tiny particles within peanut butter create an unexpectedly effective polish! Just rub a little peanut butter onto the scuff, give it a good buff, and your shoes will be almost good as new. The key here is a very small amounttoo much, and you might find yourself wearing your breakfast to class.
Ever had a zipper stubbornly stuck at the worst possible moment? Peanut butter might just save the day. All you need is the tiniest bit of it on the zippers teeth; its oils act as a natural lubricant. Peanut butter even makes it onto the list of DIY hacks for emergency hair gel. In a pinch, its sticky and holds up better than youd think. Granted, peanut butter hair gel might attract curious stares from passing dogs, but sometimes, getting that perfect hairstyle is worth it.
If youve got kidsor younger siblings to entertainyouve probably tried every craft material under the sun. Enter peanut butter: an edible, peanut-scented playdough. All you need to do is mix it with powdered sugar and some corn syrup, and voilà, hours of fun for the little ones. Just be prepared for things to get messy. Picture this: little peanut-buttery fingerprints on every surface in sight, tiny hands everywhere, and a minor crisis involving a peanut-butter-coated couch. It might not be the tidiest idea, but kids love it (until they inevitably smear it in their hair).
Another surprising trick is peanut butter as an adhesive remover. Got a price tag that just wont peel off without leaving that annoying residue? Smear a little peanut butter over it, let it sit for a few minutes, and then wipe awayno more sticky residue. Peanut butters oil breaks down adhesives, turning a household annoyance into a chance to celebrate your peanutty problem-solver.
On to the DIY spa front: peanut butter face masks. While people are used to avocados, honey, and other natural ingredients for skincare, peanut butter can surprise you as a quick-and-easy face mask. Rich in healthy fats, it provides a smooth, moisturizing effect on the skin. But heres the caveat: dont leave it on too long, and be prepared for the smell to linger. Maybe keep this one as a solo spa day activity rather than a party trick with friends.
Need to free yourself from a pesky ring that wont budge? Before you reach for the soap or ice, give peanut butter a try. Apply a small amount to your finger, and the ring should slide right off. The oils do the trick, and while it may sound messy, its actually quite effective in a pinch.
Heres one for pet owners: distracting your dog during grooming sessions. Dab a bit of peanut butter on a spoon, and hold it out while you clip their nails or give them a bath. Peanut butter is the ultimate dog treat, so it keeps them preoccupied long enough to get through those tricky grooming moments. Just remember to clean up the trail of drool theyll leave behind.
Perhaps the most bizarre use of all? Peanut butter stress-relief putty. During exam season, college students have been known to use peanut butter as a makeshift stress ball. Grab a jar, plunge your fingers in, and squeeze. The soft, pliable texture combined with the mild nutty aroma works surprisingly well to soothe frazzled nerves, and theres something strangely satisfying about the squishy sensation. Just dont forget to wash your handspeanut-butter-covered notes probably wont earn you any extra credit.
And finally, as a "hidden scent" deodorizer! Strange as it sounds, peanut butter can cover up smellsparticularly for cutting boards and certain containers. Rub it onto your board, let it sit for a minute, then wash it off. Goodbye, garlic smell; hello, subtle peanut aroma.`
},
{
name: "Peanut Butter: The Secret Ingredient in Ancient Egyptian Mummification?",
id: '39db8bea-94e6-49cc-ad54-8762ac645889',
show: true,
content: `Peanut butter, beloved spread of breakfast tables worldwide, has seen its fair share of odd uses. But none could be stranger than the theory that surfaced in the niche corners of academia in 2024: the idea that ancient Egyptians might have used peanut butter as an ingredient in the mummification process. It started as a whisper among eccentric Egyptologists but quickly spiraled into an obsession that rattled the very core of historical scholarship.
The first hint of this bizarre theory came when Dr. Penelope Wensworth, a renowned but quirky historian with a penchant for conspiracy theories, published a paper in the Journal of Unlikely Archaeological Hypotheses. The paper, titled "Nutty Preservation: A Reevaluation of Egyptian Mummification Practices," proposed that early Egyptians experimented with various substances, including honey, oils, and even mashed legumes, in their quest for the perfect embalming formula.
The hypothesis might have been dismissed as a desperate attempt to get more readers if not for one pivotal discovery.
The Mysterious Hieroglyphics of Tomb KV65
It all started with an excavation in the Valley of the Kings, where an international team uncovered an unfinished tomb labeled KV65. Among its faded murals were hieroglyphics that had long defied translation. Dr. Amir Hussein, an expert in Middle Kingdom art, noticed a particular set of glyphs that depicted a scribe holding what looked like an amphora next to a stylized representation of a jar. Inside the jar was an image that struck him as peculiara spiral, surrounded by dots.
No way, he muttered under his breath during the initial analysis. To the untrained eye, it could have been a container for oils or wine, common enough depictions in burial art. But Dr. Hussein had grown up in Cairo with a taste for fool-proof peanut butter sandwiches and recognized the swirled depiction instantly. It was unmistakable: a jar with the iconic texture of peanut butter.
The Lab Analysis
Word spread fast. The Institute of Pseudo-Historical Studies received funding from a mix of amused and intrigued benefactors, eager to either debunk the theory or lean into the spectacle. Samples were taken from the preserved jars found in KV65 and sent to Dr. Harriet Mendez, a biochemical archaeologist specializing in ancient organic residues.
When Dr. Mendez conducted a comprehensive gas chromatography-mass spectrometry test, the results were shocking. Amidst the more expected compounds like myrrh and frankincense, traces of Arachis hypogaeathe groundnut plant, from which peanut butter is derivedwere detected. The report ignited a frenzy of debate in academic circles. Could it be that the Egyptians, known for their elaborate embalming rituals, imported peanuts from as far as South America, or did they perhaps develop a similar leguminous plant on their own?
A Modern Peanut Butter Scandal
Social media was ablaze. Peanut butter enthusiasts claimed that their favorite snack now had roots in a 3,000-year-old tradition. Twitter trended with hashtags like #PeanutButterPharaoh and #AnubisApproved. The peanut butter industry seized the opportunity to market their products as timeless, with labels boasting phrases like, Embalmed with the Legacy of Kings and Taste What Pharaohs Preserved.
Historians, however, were split. Dr. Mendez, exhausted from back-to-back interviews, released a statement warning against over-exaggeration. There is still no conclusive evidence to suggest that peanut butter, as we know it, was used in full-scale embalming processes, she insisted. But that didnt stop the flow of peanut-themed conspiracy theories. Amateur archaeologists began claiming that other murals depicted Anubis, the god of mummification, handing jars of peanut butter to grateful souls in the afterlife, as if the spread had mystical properties.
The Deciphering of the Nutty Papyrus
Just when the debate seemed to reach its peak, another bombshell dropped. Dr. Husseins colleague, linguist Emma Truong, uncovered a papyrus scroll buried deep in the archives of a forgotten museum in Luxor. It was old, damaged, but remarkably detailed in its references to sticky golden paste used to bind linen wrappings more effectively and envelop the royal scent. Emma and her team spent sleepless weeks translating the scroll, which described an experimental embalming process where this paste was mixed with myrrh and cinnamon to give bodies a fragrant, nutty preservation.
The sticky properties of this paste not only helped with the wrapping but also served as a barrier against decay, Emma explained to an audience of stunned colleagues at a conference. And, dare I say it, the ancient Egyptians might have had the first recorded peanut butter recipeor something very close to it.
Historians, Squirrels, and Skeptics
Of course, not everyone was convinced. Renowned Egyptologist Dr. Benjamin Clarke held a televised debate, dismissing the idea as a hoax or, at best, a mistranslation. The Egyptians valued complex processes and wouldnt have relied on something as trivial as peanut butter in their sacred rites, he thundered.
But his argument faltered when a viral video from the archives showed a group of excavation workers chuckling at a particularly insistent squirrel sneaking peanut butter sandwiches at the dig site. Isnt it funny, one worker remarked off-camera, how even this little guy knows whats valuable here? The peanut butter had found its way back to its ancient stomping grounds, one sandwich at a time.
Legacy or Legend?
The world still debates whether peanut butter played a significant role in mummification or if it was an obscure experiment that made its way into tomb art and burial texts. Whats certain is that the idea captured public imagination like never before. Some historians now joke about stocking jars in their research rooms, just in case. Peanut butter, in all its creamy and crunchy glory, transcended its breakfast-table origins and entered the hallowed halls of historyreal or imaginedas the spread of the pharaohs.`
},
{
name: "The Great Peanut Butter Heist of 2024: How One Squirrel Nearly Ruined Breakfast Time",
id: 'c5723072-d8eb-4fff-afaa-527d84e80f1e',
show: true,
content: `The morning started just like any other at Dormitory 7B of VUBs student housing. Sleepy-eyed students shuffled into the shared kitchen, cradling mugs of coffee like life support machines. The scent of burned toast mingled with the sharp tang of instant noodles as an eclectic breakfast symphony began to play out. Lucas, known around the dorm for his meticulous breakfast routine, stood by the counter, assembling the days pièce de résistance: a peanut butter and banana sandwich.
It was a special jara creamy, artisanal peanut butter that Lucas had splurged on during a weekend market trip. Imported from a small farm in Madagascar, it promised unparalleled nutty perfection with each bite. Hed hidden it behind a fortress of hummus and mystery sauces in the communal fridge, believing it was safe. Little did he know, somethingor rather, someonehad other plans.
The security footage later reviewed by Lucas and his dorm mates would reveal everything. The day before the heist, an unusual visitor had scurried into the kitchen when one of the freshmen, nose buried in their phone, left the balcony door wide open. It was Theodore, an unusually plump and enterprising squirrel, notorious in the student blogosphere as The Phantom Forager. He had been sighted stealing everything from protein bars to blueberry muffins but had never been caught in the act.
Theodores eyes glistened as he surveyed his surroundings. There were easy marksopen packets of chips, a forgotten apple corebut Theodore was no common thief. His snout twitched as he caught a whiff of something extraordinary. He followed his nose to the fridge, pawed at the door's edge, and then with a well-practiced flick of his tail, slipped inside.
Monday morning arrived. Lucas hummed Les filles du bord de mer as he made his way to the fridge. He swung the door open and stopped mid-hum, his eyes narrowing in disbelief. The peanut butter jar was gone. A trail of tiny, sticky paw prints led from the fridge to the windowsill. His heart sank.
Panic set in as the implications unraveled. The peanut butter was not just a luxury; it was his secret weapon against grueling mornings packed with classes and CTF preparations. A few dorm mates, drawn by the commotion, gathered around as Lucas recounted the disappearance.
Wait, are you saying The Phantom Forager struck again? one of them whispered.
The crowd buzzed with excitement. The story of Theodore, the peanut-butter-loving squirrel, had reached legendary status. Lucas wasnt just facing a missing snack; he was up against an icon.
Determined not to be bested, Lucas mobilized a search party. It included Theo, the computer science major with a knack for homemade surveillance systems, and Maria, a literature student who claimed to understand the mind of a rogue. Armed with peanut butter-scented traps, homemade squirrel repellent, and a borrowed drone, they set out to catch Theodore red-handed.
The operation spanned two full days. Frustration mounted as Theodore evaded capture, seemingly mocking them by moving the traps and leaving tiny claw-written notes that read, Nice try! Lucas felt the weight of defeat looming. His dream of tasting that smooth, rich, Madagascan peanut butter seemed destined for failure.
On the third night, the team decided to set a trap worthy of Theodores cunning. They borrowed a speaker from the communal lounge and played a recording of crinkling snack wrappers. From the bushes, Theodore emerged, his tail quivering in anticipation. He paused at the sight of a trail of crackers leading up to the prized jar of peanut butter, now sitting wide open on the balcony ledge.
He pounced. The second he touched the jar, a net dropped from above, wrapping him in a peanut-butter-scented embrace. Victory! Lucas rushed forward, eyes alight with triumph, but before he could celebrate, Theodore performed an acrobatic maneuver only a squirrel with a PhD in Escape could pull off. The net tore, and Theodore leaped off the ledge with the jar, parachuting down using what appeared to be an old plastic bag.
The dorm was left in stunned silence as the daring thief bounded across the quad, peanut butter prize clutched tightly. Lucas leaned on the balcony rail, watching as his snack, and a piece of his pride, disappeared into the campus greenery.
A few days later, a new post appeared on the student blog: Theodore Strikes Again: The Story Behind the Great Peanut Butter Heist. It featured a grainy photo of Theodore perched on a tree branch, licking his peanutty bounty. Lucas, while still a bit heartbroken, couldn't help but smile. He had become part of campus folklore, just another character in the legend of The Phantom Forager.
Rumor has it Lucass next CTF challenge involved finding a hidden cache of Theodores treasure, complete with paw-print clues and riddles only the cunning could solve.
`
},
{
name: "The Battle of Spreads: Peanut Butter vs. Avocado Toast Which Will Rule the Breakfast Table?",
id: '5deaa4f7-12a7-4676-8335-a6a873b3b1bb',
show: true,
content: `The breakfast table, once a harmonious space of cereal bowls and egg platters, had become a battlefield in the age-old debate: peanut butter or avocado toast? It was a conflict that divided dorm rooms, sparked debates at brunch spots, and even infiltrated family gatherings. Both sides had their champions and their ardent critics, and now, it had escalated to the most dramatic showdown imaginable—a courtroom trial, complete with gavel bangs and wacky witnesses.
The courtroom was a surreal scene. On one side stood Peanut Butter, represented by a sentient, smooth jar in a tiny suit, oozing confidence. Its label gleamed under the fluorescent lights, proclaiming "Packed with Protein!" On the other side was Avocado, looking a bit confused, holding a pit as though it were a gavel of its own. It wore a monocle, an attempt to appear wise and refined, though it was clearly unused to legal proceedings.
The honorable Judge Pancake presided over the case, his golden surface glistening with butter. He called for order as murmurs from the gallerya mix of college students, nutritionists, and social media influencersfilled the room.
First to take the stand was Peanut Butter, supported by its entourage of animated almonds, peanuts, and health nuts. The jar puffed up with pride as its attorney, a sharp-talking slice of bread named Rye OToole, presented Exhibit A: Nutritional Superiority. A slide showed that peanut butter was not just a comfort food but a powerhouse, loaded with protein and healthy fats. Rye OToole pounded the podium and declared, Who here hasnt felt the post-workout glory of a peanut butter smoothie, hmm? Sustenance! Energy! A champions breakfast! The nuts in the audience applauded, causing Judge Pancake to rap his gavel for silence.
Avocados defense was not to be underestimated. Represented by an earnest piece of sourdough toast, whose name tag read Cris P. Crust, it argued that avocado was the epitome of the modern breakfast. Cris P. Crust projected an image of a perfectly Instagrammed avocado toast, sprinkled with chili flakes and drizzled with olive oil. Social prestige, your honor. Nutritional value with flair! A breakfast that whispers sophistication with every bite, Crust stated, adjusting its glasses with dramatic flourish.
The gallery buzzed. A young influencer with blue hair whispered to her friend, Honestly, avocado toast is so yesterday, but it does get likes. A protein-shake enthusiast countered with, Peanut butter is the original king of gains.
Things took a turn when celebrity endorsements were called in. First up was Banana, a longtime ally of Peanut Butter. Banana sauntered in with a confident swing, testifying to their collaborative dominance in breakfasts, smoothies, and even desserts. Peanut butter is my ride-or-die, Banana declared. Weve conquered snacks from toast to oatmeal. Applause erupted once more, cut short by Judge Pancakes stern glance.
Then, Avocado called its own surprise witness: Tomato, representing the controversial avocado-tomato toast hybrid that blurred the line between breakfast and lunch. Tomatos testimony was passionate but polarizing. We add zest, color, and antioxidants! We bring balance! But someone in the back muttered, Thats just bruschetta! and Judge Pancake shook his head.
The case reached its climax with a set of meme submissions, presented as evidence. Peanut Butters memes were wholesome and nostalgic, full of childhood lunchboxes and relatable peanut butter jar struggles. Avocados memes, on the other hand, were trendy and cynical, often poking fun at their own overpriced reputation. One particularly viral meme that flashed on the screen read, Cant afford a house, but at least I have my avocado toast. It earned a mix of laughter and sighs from the gallery.
In closing arguments, Rye OToole called for a return to basics. Peanut butter is the spread of resilience. A spread that has seen world wars, economic depressions, and still sticks by your sideliterally and figuratively. Cris P. Crust countered with elegance: Avocado embodies health, forward-thinking, and a touch of luxury. Why settle for simplicity when you can elevate your mornings?
The jury, made up of a balanced mix of breakfast pastries and a bowl of skeptical granola, deliberated as the room held its breath. Judge Pancakes expression remained neutral, though a rogue drip of syrup slid down his face, signaling that even he felt the tension.
After what seemed like an eternity, the verdict was announced. The jury ruled it a drawboth peanut butter and avocado toast were champions in their own right, representing different aspects of breakfast culture. A cheer rose from both sides, followed by a collective sigh of relief. Peanut Butter shook hands with Avocado, an act that squished them together briefly, creating an unexpected blend that would later become the trendiest new toast topping: Peanutocado Spread.
Thus, the battle of the spreads ended not in defeat but in a newfound harmony. Social media trended with #Peanutocado, and influencers were quick to adapt. The courtroom, now silent, had been a battleground for breakfast supremacy but had ultimately brought about a peace treaty fit for any table.
`
},
{
name: "Do you know the secret of peanut butter?",
id: 'bee229bc-4147-4c34-9418-9572e9f0ee1b',
show: false,
content: "IGCTF{M4d3_fr0m_p3Anuts}"
},
{
name: "The Conspiracy Behind Smooth vs. Crunchy: What Big Nut Doesnt Want You to Know",
id: '3eafb5c1-7bbd-4607-86f3-750672d98d75',
show: true,
content: `It started innocently enough, as debates often do. Friends arguing in kitchens, families divided at the breakfast table—smooth or crunchy, which was better? For years, it seemed like a harmless preference, a quirky conversation starter. But beneath the surface of this seemingly benign choice, there was a storm brewing that would shake the peanut butter industry to its core. Enter Big Nut, the shadowy organization pulling the strings.
Big Nut wasnt just a nickname for the conglomerates dominating the peanut butter market; it was a clandestine network of decision-makers, ad agencies, and food scientists with one goal: control consumer behavior. They wanted everyone to think it was just a matter of taste. But in truth, they were waging a silent war over profits, and the battleground was spreadable.
The conspiracy unraveled thanks to a whistleblower named Charlie Crunch OMalley, a third-generation peanut farmer from Georgia. Charlie had grown up surrounded by rows of legumes and jars of homemade peanut butter, never imagining hed become the key to one of the most bizarre scandals in snack history. It started with a simple email from a supplier labeled Confidential: Re. Textural Consistency Directives. Intrigued, Charlie opened it and discovered a trove of memos outlining Big Nuts covert experiments to manipulate public perception.
Theyre engineering the perfect bite, but its not about the customer, Charlie revealed in a shaky interview, filmed in a dim barn with peanut plants rustling behind him. Its about making us dependent on their products, choosing what we think we prefer. The documents outlined how flavor profiles, oil ratios, and even the subtle differences in peanut grind size were crafted not for nutrition or taste but for maximum sales.
The memos didnt stop there. They detailed a media strategy designed to fuel debates. A series of carefully placed opinion pieces in lifestyle magazines would praise the purity of smooth one week, followed by op-eds extolling the boldness of crunchy the next. The cycle was engineered to keep consumers emotionally invested, never noticing that their choices were orchestrated.
The story took a turn when Dr. Nathaniel Jiferson, an eccentric food chemist and former Big Nut insider, decided hed had enough. Dr. Jiferson, who wore a lab coat splattered with peanut oil stains and spoke with the fervor of a man who hadnt seen daylight in weeks, emerged from his research bunker with a manifesto titled The Ground Truth. He claimed Big Nut was experimenting with additives to create an invisible loyalty agent that would trigger a preference for one type over the other, thus splitting consumer loyalty and doubling market engagement.
Theyve perfected a blend of compounds that can only be described as neuro-nutty, Jiferson said, waving a half-empty jar as if it were a piece of damning evidence. One week, youre a smooth loyalist; the next, youre dipping spoons straight into the crunchy. Its psychological warfare on your palate.
The manifesto spread quickly through underground food forums, alarming casual snackers and peanut butter connoisseurs alike. The most outrageous claims involved celebrity endorsements: videos of pop stars casually enjoying peanut butter in sponsored content were alleged to contain subliminal audio frequenciesa peanut siren song that made viewers crave one type or the other.
As the revelations gained traction, PeanutCon, the largest annual convention of peanut enthusiasts, became a hotbed of tension. What was once a jovial affair of tastings and spread-ranking contests turned into a battleground. Smooth loyalists, dressed in silky, beige shirts emblazoned with Smoothers for Life, faced off against crunchy supporters clad in textured, hand-knitted sweaters marked with Crunch On! Signs read, Down with Big Nut! and Freedom of Choice, Not Manipulation!
Charlie OMalley made an appearance, supported by a ragtag group of independent farmers with jars labeled with handmade tags that read Honest Nut. He gave a rousing speech, his voice hoarse from shouting over the uproar. Were here to take peanut butter back! Well grow, grind, and spread it the way our grandmothers did, without interference!
The climax of PeanutCon came when Dr. Jiferson revealed a final twist: Big Nuts real plan was to introduce a third categoryUltra-Smooth. They want to end the debate by creating a new allegiance, Jiferson declared, his eyes wild with both conviction and sleep deprivation. Ultra-Smooth is smoother than smooth, engineered to glide so perfectly that youll forget crunchy ever existed.
The crowd gasped, though a few attendees, cautiously curious, whispered, Id try it once.
Meanwhile, Big Nuts PR representatives issued a statement that they were merely pioneers of choice. Consumers deserve options, and we provide them, the statement read, the corporate jargon as smooth as their bestselling spread. They denied any tampering with consumer psychology, attributing any perceived loyalty shifts to natural market trends.
For a while, the controversy captivated the nation. The peanut butter aisle became a place of whispered debates and side-eyed glances, shoppers suspicious of what their choices said about them. Sales surged, ironically, as both sides stocked up to show allegiance.
In the end, the war didnt end; it simply retreated into kitchen cabinets and breakfast nooks, where jars of smooth, crunchy, and maybe one day Ultra-Smooth sat side by side. People spread their choices onto bread, sometimes defiantly, other times thoughtfully. And though the shadows of Big Nut still lingered, one thing was certain: the battle had made everyone think twice before dipping a knife into their chosen jar.`
}, {
name: "The other, even more secret secret of peanut butter",
id: 'ebddb7db-b20a-4e8f-b9e9-954e1c07ab83',
show: false,
list: false,
content: `IGCTF{Pe4nut_Butt3R_1s_n0t_made_froM_butTEr}`
}
]
const getPostTitles = (req, res) => {
res.json(posts
.filter(post => post.list !== false)
.map(post => ({ name: post.name, id: post.id, show: post.show })));
}
const getPost = (req, res) => {
const postid = req.params.postid;
const post = posts.find(post => post.id === postid);
if (post) {
res.json(post);
} else {
res.status(404).send('Post not found');
}
}
router.get('/', getPostTitles);
router.get('/:postid', getPost);
export default router;

View File

@ -0,0 +1,17 @@
import { Router } from 'express';
const router = Router();
const getProfile = (req, res) => {
const userName = req.params.username;
res.cookie('token', "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiQ1RGIFBhcnRpY2lwYW50IiwibGlrZXNfcGVhbnV0X2J1dHRlciI6ZmFsc2V9._DG-nLXVTzNw_BoSQ240P6QNL9JbxRz6aWAgPFiXfVU");
res.json({
id: userName,
flag: userName === 'admin' ? 'IGCTF{Re4lly_any_fru1t_exc3pt_f0r_Mang03s}' : 'only admins get to see the superior fruit',
orders_amount: userName === 'admin' ? 0 : userName.length * 12,
});
}
router.get('/:username', getProfile);
export default router;

View File

@ -0,0 +1,9 @@
services:
express-backend:
build: ./backend
network_mode: service:nextjs-frontend
nextjs-frontend:
build: ./frontend
ports:
- "3000:3000"

View File

@ -0,0 +1,7 @@
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git

View File

@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

View File

@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -0,0 +1,66 @@
# syntax=docker.io/docker/dockerfile:1
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View File

@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@ -0,0 +1,21 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -0,0 +1,28 @@
import type { Metadata } from "next";
import {Sour_Gummy} from 'next/font/google'
import "./globals.css";
const sourGummy = Sour_Gummy({
weight: "400",
subsets: ["latin"],
})
export const metadata: Metadata = {
title: "Peanut Butter",
description: "Everything about Peanut Butter",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${sourGummy.className} antialiased w-screen h-screen`}
>
{children}
</body>
</html>
);
}

View File

@ -0,0 +1,48 @@
"use client";
import Link from "next/link";
import { useEffect, useState } from "react";
export default function Home() {
const peanutsImageUrl = "https://images.pexels.com/photos/209371/pexels-photo-209371.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
const [blogPosts, setBlogPosts] = useState<{ id: string, name: string, show: boolean }[]>([]);
useEffect(() => {
const fetchPosts = async () => {
const response = await fetch("/api/posts/");
setBlogPosts(await response.json());
// setBlogPosts([{ id: "1", name: "Hello world", show: true }]);
console.log(blogPosts);
};
fetchPosts();
}, []);
return (
<div className="w-full h-full pt-12 pl-16 flex flex-col items-start" style={{ backgroundImage: `url(${peanutsImageUrl})`, backgroundSize: 'cover' }}>
<div className="flex items-center mb-7">
<div className="text-7xl pr-6">🥜</div>
<div className="flex flex-col">
<h1 className="text-7xl">Peanut Butter</h1>
<h2 className="text-3xl">For evolved people only</h2>
</div>
<div className="text-7xl pl-2">🥜</div>
</div>
<div className="flex space-x-20">
<div>
<h3 className="text-2xl mt-4 font-bold">Profile</h3>
<Link href="/profile/guest" className="text-lg underline">My peanut butter profile</Link>
</div>
<div>
<h3 className="text-2xl mt-4 font-bold">Blog posts</h3>
{blogPosts.map((post) => (
(<div key={post.id}>
{post.show ? <div className="flex space-x-1 items-baseline">
<p>🥜</p>
<Link href={`/posts/${post.id}`} className="text-lg underline">{post.name}</Link>
</div>
: <></>}
</div>)
))}
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,34 @@
"use client";
import { useEffect, useState } from "react";
export default function PostPage({ params }: { params: Promise<{ postid: string }> }) {
const [title, setTitle] = useState<string>("");
const [content, setContent] = useState<string>("");
useEffect(() => {
const fetchPost = async () => {
const allPostsResponse = await fetch("/api/posts");
const allPosts = await allPostsResponse.json() as { id: string, name: string, show: boolean }[];
const p = await params;
if (allPosts.find(post => post.id === p.postid) === undefined) {
setTitle("Post not found");
setContent("This post does not exist, or you don't have access.");
return;
}
const response = await fetch(`/api/posts/${p.postid}`);
const post = await response.json();
setTitle(post.name);
setContent(post.content);
};
fetchPost();
}, []);
const peanutsImageUrl = "https://images.pexels.com/photos/6659880/pexels-photo-6659880.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
return (
<div className="w-full h-full pt-12 pl-16 fixed overflow-y-scroll" style={{ background: `linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.8)), url(${peanutsImageUrl})`, backgroundSize: 'cover', backgroundAttachment: "fixed" }}>
<div className="absolute flex flex-col items-start pb-32">
<h1 className="text-3xl font-bold mb-5 mr-5">{title}</h1>
<p className="whitespace-pre-wrap w-3/5 text-lg">{content}</p>
</div>
</div>
);
}

View File

@ -0,0 +1,65 @@
"use client";
import { useEffect, useState } from "react";
export default function ProfilePage({ params }: { params: Promise<{ username: string }> }) {
const [flag, setFlag] = useState<string>("");
const [ordersAmount, setOrdersAmount] = useState<number>(0);
const [brand, setBrand] = useState<string>("");
const [username, setUsername] = useState<string>("");
useEffect(() => {
const fetchPost = async () => {
const p = await params;
setUsername(p.username);
const response = await fetch(`/api/profile/${p.username}`, {
credentials: 'include',
});
const post = await response.json();
setFlag(post.flag);
setOrdersAmount(post.orders_amount);
};
fetchPost();
}, []);
const getBrand = async () => {
const response = await fetch("/api/brand", {
credentials: 'include',
})
const responseJson = await response.json();
setBrand(responseJson.brand);
};
const peanutsImageUrl = "https://images.pexels.com/photos/6659867/pexels-photo-6659867.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2";
return (
<div className="w-full h-full pt-12 pl-16 fixed overflow-y-scroll" style={{ background: `linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.8)), url(${peanutsImageUrl})`, backgroundSize: 'cover', backgroundAttachment: "fixed" }}>
<div className="absolute flex flex-col items-start pb-32">
<h1 className="text-5xl font-bold mb-5 mr-5">Your profile</h1>
<div className="flex space-x-6">
<div>
<img src="/avatar.png" alt="avatar" width={300} height={300} className="rounded-xl" />
</div>
<div className="flex flex-col space-y-3">
<div>
<p className="text-xl">Your account</p>
<p>{username}</p>
</div>
<div>
<p className="text-xl">Your purchases</p>
<p>{ordersAmount} orders</p>
</div>
<div>
<p className="text-xl">The best fruit</p>
<p>{flag}</p>
</div>
<div>
<p className="text-xl">The best brand</p>
<div className="flex space-x-3">
<a onClick={getBrand} className="text-orange-700 cursor-pointer bg-white rounded-xl px-3 py-0">Retrieve</a>
<p>{brand}</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,16 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: "standalone",
rewrites: async () => {
return [
{
source: "/api/:path*",
destination: "http://localhost:8000/:path*",
},
];
},
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
{
"name": "peanut-butter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106",
"next": "15.0.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "15.0.3"
}
}

View File

@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

View File

@ -0,0 +1,18 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;

View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}