首页 > 其他分享 >2023 r00t 奶茶题

2023 r00t 奶茶题

时间:2023-01-19 23:22:51浏览次数:52  
标签:__ 文件 r00t py 模块 json cookie 奶茶 2023

2023 r00t 奶茶题

URL

python

  • cls代表类本身,self代表类实例;

    @classmethod
    def func(cls, ...):
    	...
    	ret
    

    前要带着 @classmethod 指定下一个函数为类方法,首参数为 cls 而非 self ,其目的就是为了调用当前的类(例如用于新建和初始化)而非调用一个已经初始化完毕的实例(只能获得参数)

  • a==None 表示a被定义了,但为空

  • __setitem____setitem__(self, key:int, value:bool) 函数在更改键值时自动先调用,key与value表示键、值,冒号标记参数类型,在常规python中只做标记,不严格遵守,在fastapi中做数据类型校验,不同时会报错

  • instance(a, b) :判断A是否继承自B

  • assert expression, "errText": 在表达式"expression"为false时会抛出错误AssertionError(errText),为True时则正常运行

  • str.rjust(width[, fillchar]) 字符串右对齐处理

  • def funcs() -> int: 箭头表示Type Hint,用于表示函数期待的输入与输出类型

解题

思路1

Cookie中的game可以反复使用,它标记了剩余的尝试次数与对应答案,但通过爆破发现,hard难度无解(多线程测试时测试数量总和5040差了几个,不知道为什么)。

扫一扫会发现host/docs可以访问,这是FastApi的自述文档,可以查看该网站的路由配置(貌似使用了路由可以防止一些敏感文件泄露,除了设定好的docs)

根据提示,项目基于"amis_helper",结构相似,"amis_helper/src/app/amis_router.py"展示了项目的路由设置,其中部分路由需要amis_admin权限,该权限需要在Cookie中设置一个"amis_pass"密码,密码存放在"amis_helper/src/conf/amis.json5"中,若没有该权限,访问部分页面会404。出题人提示flag存放在"1a2b/flag"下,访问时出现404,推测需要获取正确的"amis_pass"才能访问,因此目标是:获取"src/conf/amis.json5"的内容

"src/conf/funcs.py"内提供了读取conf文件的函数"read_conf",寻找调用该函数的方法进而获得amis_pass

废话了这么多,这条思路8行(233)

思路2

经过出题人不厌其烦的主动被社工,又摸又菜的内鬼终于找到了题目的入口(?)

题目首页的图片提示有一个"/photo/file"入口可以访问file文件,在基项目中路径的传入与获取通常有path与str两者,前者可以直接写URI,可以写 http://xxx/xxx ,也可以直接传 /etc/passwd (获取源码后会发现基项目中的pages/path写法和题目源码其实不一样,因此尽管基项目的path传入看起来没洞,但题目的photo入口是有洞的),尝试传入 /photo//etc/passwd 能成功访问(传入的路径不对会回显500,后台服务器在执行时抛出了错误),尝试相对路径,在基项目的最顶层目录中有一个"run.py",尝试目录穿越获取该文件:pages/../../run.py ,成功获取该文件后按照基项目结构下载源文件,已知文件夹,可以通过python中的相对引用来获得文件夹下的源码名称。

在python中文件夹要想被识别为包(可相对引用并导入)需要包含文件"__init__.py",该文件用于导入该包内各模块的函数,上级目录导入该模块时可以直接通过包访问到这些函数(跳过了模块)。除此之外包内模块也会引用一些没导入识别文件(__init__.py)的模块,不断从已知的模块获取新模块就能得到完整的模块列表(如果一个模块是被上层间接调用的,一定要包含在这条链中;直接调用也能在上层模块中直接找到;若是单独运行的脚本就没办法了)。

模块分析

"app/game_router.py"是1a2b游戏的路由源码,"conf/amis.json5"存放着之前想要得到的amis_pass,但该密码只能用于修改前端页面,无法用于获取flag(path/all_pages无效,flag惨遭过滤也不能访问)

"app/photo_router.py"是photo路由的源码,里面可以看到函数 os.path.join("./static/photo/", path) 直接将路径进行拼接用于搜索文件,因此可以目录穿越(但不知道为什么可以直接从"/"目录开始传递路径,可能是path类型识别参数时考虑到了这种情况)

"utils/classes.py"中创建了一个Bitmap类,以bit位来存储特定的数据。
"model"包中,"XAXB"模块中的Choices类基于Bitmap,用于获取游戏结果,同时也给baby模式提供了baby的逻辑;"xAxB"类是游戏主类,提供了a,b的计算方式;"cookie"模块中的"GameCookie"是所有cookie类的基类,它提供了cookie状态与cookie编码相互转换的函数;三个难度各有其cookie类,继承自基类。

漏洞分析

一般模式下,Bitmap会将正确数字标记为True,只有正标记的位置会与玩家上传的guess进行比较得出"xAxB"的结果,不同的xAxB在一个字典中以键的形式,对应一个Choices,该Choices有一个True_num参数标记正确的数字位的个数,最后选取True_num最多的"xAxB"对象作为结果返回;baby模式下所有数字位都被正标记,上传的guess将与10000个数字全部比较一遍,统计出来True_num最大即情况数量最多的一个"xAxB"将被作为结果,也是guess与这10000个数字中的某一个最有可能的情况,由于"4A0B"最多就一个,因此这是不可能出现的。

漏洞主要出现在cookie的解析上,cookie是将cookie类用pickle.dumps取下来再用base64加解密来转换的,只要类型一致上传的cookie类内容怎样都行,因此我们修改cookie类下的choices的正标记为 [0] 对应"0000",上传cookie后传入guess="0000"就能通过。这里会遇到以下问题:

  1. 修改cookie类下choices的size不行,该size决定统计时遍历的数量,若只遍历1次也能取到4A,但该参数用于比较变量长度,上传给服务器后能解析,但在运行时会出错,因此会回显500状态码:"Internet Server Error"
  2. 为了伪造cookie,我们需要获取完全一致类结构,通过直接运行来获取cookie;pickle 在序列化对象时会加入其路径信息,包括调用模块的导入情况,因此必须遵守源文件的源码结构直接调用才能获得完全一致的cookie信息;为了逐层调用到"model/cookie.py"文件,可以在"amis_pyy"下写一个"getCookie.py"直接调用cookie中的相关函数来获得,也必须在这一层编写该函数,理由见后文关于python相对引用的分析
  3. pickle 在序列化时不考虑类的函数,而是考虑对象的参数与继承关系所带来的附加参数,同时也会把类对象的路径也打包进去,记录下它是根据那个目录下的哪个类进行封装的,同样解析时也要找到对应目录下的对应类进行解析还原(不清楚绝对引用与相对引用是否有影响),因此需要保证文件结构的一致

参考:[

Web信息搜集

]

Python相对引用

为了逐层调用到"cookie.py",我被python的相对引用折磨了好久。这不是个复杂的问题,但研究起来让人很不解:这么聪明的python怎么引用的时候像个笨蛋。

例如"test_parent"有如下文件结构:

test_parent
│  main.py
│
└─test
    │  src.py
    │  __init__.py
    │
    ├─subPac
    │  │  src1.py
    │  │  src2.py
    │  │  src4.py
    │  │  __init__.py
    │  │
    │  └─__pycache__
    │          src1.cpython-310.pyc
    │          src2.cpython-310.pyc
    │          src4.cpython-310.pyc
    │          __init__.cpython-310.pyc
    │
    ├─subPac1
    │  │  src3.py
    │  │  __init__.py
    │  │
    │  └─__pycache__
    │          src3.cpython-310.pyc
    │          __init__.cpython-310.pyc
    │
    └─__pycache__
            __init__.cpython-310.pyc

绝对引用:

# src.py
import  subPac
from subPac1 import src3.py

相对引用:

# src1.py
import .src2
# from . import src2
from .src4 import fun4
from ..subPac1 import src3	# <?-1

用"."来表示导入位置的成为相对引用,表示相对于源码文件其他包和模块所在位置;绝对引用则是引入包时不用".",直接导入称为绝对引用,绝对引用后被引用的包下会出现 __pycache__ 文件夹。

当python源码被直接运行时,我们称之为脚本,此时在该文件中有 __name__=__main__ 表示它是程序运行的入口文件;当python文件被导入到其他源码中调用时,它称为模块,自然有 __name__!=__main__

相对引用必须放在模块中使用,若在脚本中使用会出现报错:"ImportError: attempted relative import with no known parent package";所有模块的起点都是一个脚本,这个脚本调用了这些模块,因此这个脚本必须使用绝对引用作为导入包的起始路径;绝对引用在模块中使用也是可行的,但过于僵硬。

上级目录引用

上述文件中 <?-1 是一处对上级目录的另一个包内模块的导入,要想正确运行该文件(src1.py),需要在该文件所在位置的上级至少两层创建调用脚本,例如"test/main.py"。若使用"src.py"去调用该文件,则会出现报错:"attempted relative import beyond top-level package" 。这是因此此时"src.py"脚本被视为最高级的包,它是树的根节点,不允许出现同级的"subPac1"。


当文件夹下有"__init__.py"时,该文件才会被视为包,文件夹下的源码文件才会被视为模块,能被包直接访问的函数需要导入到"__init__.py"文件中,例如:

# subPac/__init__.py
from .src1 import func1

在"main.py"中就能直接导入"subPac.func1":

# main.py
from subPac import func1

访问了subPac包就会访问"subPac/__init__.py"文件,由于其中的导入又会访问"src1.py,src2.py,src4.py"和"subPac1/src3.py"等文件

参考:[

Windows命令行 查看文件树结构

Python-import导入上级目录文件

Relative imports for the bilionth time

解决导包报错:ModuleNotFoundError,ImportError

]

Json-fastApi的前端json上传

拿到了amis_pass就想加个彩蛋,没想到fastApi的json给我带来了如此大的震撼

用POST上传json时类型为:"application/json"
上传的内容:

{
 "path":"string",
 "title":"string",
 "json":"string",
}

json的主体内容必须使用双引号!这是硬性格式要求!

打包json的代码:

import json
import os
obj={...}
with open(os.path.dirname(__file__)+"/newJson.txt", "w", encoding="utf-8") as f:
    f.write(json.dumps(obj, sort_keys=True, indent=1, separators=(',', ': '), ensure_ascii=False))
    f.close()

indent控制输出的缩进空格个数,得到的文件"newJson.txt"需要按如下步骤操作:

  1. 每次替换前在目标字符前加一个额外的字符做分隔符,替换完再删除分隔符;我用VSC的搜索替换,当两个被搜索目标相邻并被删除后再替换,则会出现光标合并,最后就会少一个字符
  2. 搜索所有 \ 字符并替换为 \\
  3. 搜索所有 替换为 \"
  4. 搜索所有换行替换为 \n
  5. 将其用双引号包裹作为json的值传入

解释:json上传后被解析一次(处理一次所有的 \x 转义,并去掉最外层的引号),得到的内容直接作为json的内容(hackbar会检查双引号是否匹配,但除此之外的奇怪语法错误会被直接搬到JavaScript中,很神奇)。json作为被解析目标,其中的字符串又会被解析一次,因此对于原来就有的 \n ,替换为 \\n 是为了第二次解析,原来就有的 \" 替换为 \\\" 也是为了二次解析,它们第二次被解析后才有用。而对于原来的 "换行符 分别被替换为 \"\n 是为了通过前端的检查(引号匹配的基本逻辑要有,解析后直接做json因此还得是双引号而不能是单引号)并保持可读性(换行与缩进将会在第一次解析就显示出来)

例如:

{
 "json":"{\"par1\":\",\" \"par2\":\"123\"}"
}

解析后得到:

json_={"par1":"," "par2":"123"}

显然这里是有语法错误的,但解析后的内容直接作为json赋值给了"json_"

标签:__,文件,r00t,py,模块,json,cookie,奶茶,2023
From: https://www.cnblogs.com/Forest-set-you/p/17062285.html

相关文章

  • 数据结构课程设计[2023-01-19]
    数据结构课程设计[2023-01-19]数据结构课程设计一、课程设计要求实现指定的题目(学号最后两位%4+1),并撰写课程设计报告。独立完成,功能不完备也没关系,只要是自己做的使......
  • SICTF2023 web_wp
    兔年大吉源码如下<?phphighlight_file(__FILE__);error_reporting(0);classHappy{private$cmd;private$content;publicfunction__construct($......
  • 2023牛客寒假算法基础集训营2题解
    写在前面菜菜,哭哭,大佬救救QaQ理解大佬的代码并且整理成一篇博客真的很累...C:Tokitsukazeanda+b=n(hard)1.本蒟蒻的代码个人感觉用前缀和更方便。我最开始用的是线......
  • 2023.1.19 学习初识 JAVA
    C语言1972年诞生了C语言,1982年诞生了C++  1995年诞生了JAVA。C语言贴近硬件,运行速度快,效率极高  (指针和内存管理)操作系统编译器数据库网络系统等C++面向对象......
  • 2023牛客寒假算法基础集训营2
    B.Tokitsukazeanda+b=n(medium)(求交集)题目大意:给定两个区间[l1,r1],[l2,r2],从两个区间各取一个数[a,b],求满足a+b==n的个数(a,b中只要一个不同就算不同的选法)......
  • 2023牛客寒假集训2
    A-Tokitsukazeanda+b=n(easy)[暴力]A-Tokitsukazeanda+b=n(easy)_2023牛客寒假算法基础集训营2(nowcoder.com)Tokitsukaze有一个整数\(n\),以及$2$个区间\(......
  • 2023牛客寒假算法基础集训营2 A/B题
    题目:简单来说就是给一个数字n,然后数字l1在一个区间,l2在一个区间求出l1和l2不同组合和为n的数量。题解:A题(easy)因为数据范围比较小,所以随便写个循环,直接遍历也能......
  • t团队日常记录 20230119
    今天买了很多菜啊,晚上母亲又拿了几样给我这冰箱。今天还算完成了这个功能了,感觉虽然痛苦并快乐着,有种种困难,包括对程序框架的从陌生到熟悉,也是个过程,包括t团队人员融......
  • 力扣每日一题2023.1.19---2299. 强密码检验器 II
    如果一个密码满足以下所有条件,我们称它是一个强 密码:   它有至少8 个字符。   至少包含一个小写英文 字母。   至少包含一个大写英文 字母。   至......
  • 2023年1月18日 模块交接会议 —— 数据收集源的扩展与否
    没有想到还是在年前开了模块交接会议,负责交接的人还是比较开心的,比较可以在年前把这个活切割赶紧,但是负责接受的人可是有些苦头了,估计这个年也过得不能消停了,而我也是这其......