1.1Python在字符串格式化的两种方式
- %
- format
1.2 %,关于整数的输出
- %o:oct 八进制
- %d:dec 十进制
- %x:hex 十六进制
1.3 关于对格式化字符串的案例解释
print("整数:%d,%d,%d" % (1, 22.22, 33))
print("整数不足5位,左边补空格 %5d " % 22)
print("整数不足5位,左边补0 %05d " % 22)
print("整数不足5位,右边补空格 %-5d " % 22, "end")
print("八进制 %o" % 222)
print("十六进制 %x" % 12)
就是用类似%d代替
包含知识点
- 当你有多个参数需要格式化输出的时候,需要用元组 ;注意不能用列表因为列表是可变的
(1,2,3)
- 若传入的是浮点数如 ,最后输出的是22,不会四舍五入哦
22.55
- 若传入了字符串 ,是会报错滴!
2.1 format格式化输出
相对基本格式化输出采用 % 的方法,format()功能更强大,该函数把字符串当成一个模板,通过传入的参数进行格式化,并且使用大括号 {} 作为特殊字符代替 %
format,位置匹配(最常用)
- 不带编号,即“{}”
- 带数字编号,可调换顺序,即“{1}”、“{2}”
- 带关键字,即“{a}”、“{tom}”
print("今天是{}的{}生日会".format("帅哥", 18))
print("今天是{1}的{0}生日会".format("帅哥", 18))
print("今天是{0}的{1}生日会".format("帅哥", 18))
print("今天是{name}的{key}生日会".format(22, 11, name="帅哥", key=18))
print("今天是{name}的{key}生日会,接着上{}".format("test", name="帅哥", key=18))
# 以下会报错
print("今天是{0}的{}生日会".format("帅哥", 18))
print("今天是{name}的{key}生日会,接着上{}".format( name="帅哥", key=18,"test"))
也就是用fomat里的参数去替换前面的{} 是一种格式化字符串方法
2.1 漏洞利用思路
首先我们暂时无法通过格式化字符串来执行代码,但我们可以利用格式化字符串中的“获取对象属性”、“获取数组数值”等方法来寻找、取得一些敏感信息。
以Django为例,如下的view:
def view(request, *args, **kwargs):
template = 'Hello {user}, This is your email: ' + request.GET.get('email')
return HttpResponse(template.format(user=request.user))
效果为显示登陆用户传入的email地址:
但因为我们控制了格式化字符串的一部分,将会导致一些意料之外的问题。最简单的,比如:
输出了当前已登陆用户哈希过的密码
那么分析一下 这里email 变成了{user.password} 也就是和 fomat 相匹配了 变成了和{user}一样 这里的{user.password} 相当于request.user.password
2.2 用格式化字符串漏洞获取Django配置信息
我能够获取到的变量只有request.user
,这种情况下怎么利用呢?
Django是一个庞大的框架,其数据库关系错综复杂,我们其实是可以通过属性之间的关系去一点点挖掘敏感信息。但Django仅仅是一个框架,在没有目标源码的情况下很难去挖掘信息,所以我的思路就是:去挖掘Django自带的应用中的一些路径,最终读取到Django的配置项。
p神的文章中写到Django自带的应用“admin”(也就是Django自带的后台)的models.py中导入了当前网站的配置文件:
所以,思路就很明确了:我们只需要通过某种方式,找到Django默认应用admin的model,再通过这个model获取settings对象,进而获取数据库账号密码、Web加密密钥等信息。
我随便列出两个,还有几个更有意思的我暂时不说:
http://localhost:8000/?email={user.groups.model._meta.app_config.module.admin.settings.SECRET_KEY}
另外p神还提到关于模板沙盒漏洞我这里只是简单记录具体移步
Python 格式化字符串漏洞(Django为例) | 离别歌 (leavesongs.com)