好久没发新文章了。正好国庆就要到了,加上还有巅峰极客跟国赛要打。所以周五晚上练手了DarkCTF的题目,稍微花了几小时ak掉当时放出的web。其中大部分都比较简单,其中一道拿了二血的nodejs题目有点意思打算稍微记录下。
给了源码跟网页,主体上就是
取UserAgent作为参数,判断了字符串长度与数值大小。简单使用科学计数法 9E9 即可绕过
给了个zip然后让你从里面的log文件里找flag.简单提取出其中String.fromCharCode部分放到浏览器console里转一下即可。不过提交时要用 darkCTF 而不是 DarkCTF
开始没啥头绪。后来发现提示了传参id.于是很简单就能发现是个sql注入。甚至基本上就是sqli-labs的第一关。union注入从users表中的password里找到某个flag.
直接提示传参id.
普通布尔盲注。没啥说的。
这题花了一些时间。不过做出来时拿了二血还是挺舒服的。看到Defenit是一血不知道是不是又被posix师傅秒掉了。(posix nodejs 永远的神)
首先题目是黑盒测试的。这点对于一个nodejs题目来说增加了不少难度。如果是我出这题可能就直接给源码了。但是做完后我感觉这种设计还是很贴近现实的。也给了我一定程度上黑盒测nodejs的手段。
首先题目大致实现了一个note功能。可以通过 addNotes 路由传参message。通过 deleteNote/{id} 来删除note.
到这一步为止我有几种思路
1.nodejs反序列化。
2.原型链污染
3.???
首先想到nodejs反序列化是因为以前NahamconCTF解出的一道node题。也是黑盒。然后只有20多解。但是我当时是抱着试一试的心态尝试了下反序列化居然成功拿到shell.后来发现,之所以会有这种想法就是因为:
数据为json串。数据由cookie控制
序列化输出的结果就是json串,所以黑盒下尝试node-serialize未尝不可。当然本题自然是失败了。因此方法也不再多说。
第二种想法自然是因为nodejs中想要出现原型链污染实在是太容易了。不过有一说一一道以原型链污染为漏洞的ctf题使用公共靶机是非常不妥的,并且一般是给出源码进行测试。加上此处并没有什么显眼的模板使用比如ejs。因此是原型链污染的可能性也不大。
此时基本上常规的思路已经走不通了。但是我简单fuzz了一下
发现它是没有做只能传字符串的限制的。一般来说,不对参数类型做限制时,我们可以传入数组或对象。因此在传入对象时这里会触发报错。
然后重点就来了,触发的报错内容如下
爆出了当前路径 /app 以及一个依赖 dustjs-linkedin/lib/dust.js
(这里多嘴一句,这里一开始defenit拿到一血后长达4个多小时都没有其他解,并且当时触发报错时题目只会爆500而不是像上面这样打印报错traceback,后来改了题才有了这个报错栈)
看到这个 dust 加上题目名中的dust,去搜索一波的话不难发现存在一个漏洞
并且有文章在实战中遇到解决
https://artsploit.blogspot.com/2016/08/pprce2.html
简单说就是,dust的模板会进入一个if语句执行 eval 。但是如果我们直接传入参数时它会把敏感字符做处理,而如果传入数组时则不会处理敏感字符,即可rce。
再多嘴一句,到确定是dust的洞这一步我本来以为弹个shell就完事了。结果测了好久发现不知道是不通外网还是没能执行。差点以为找错洞了。直到他改了题才发现dust依赖确认没找错洞,专心测下去的。
当然没有报错我们也有几种办法确认此处存在dust的eval漏洞
比如
会导致
触发报错
再比如
也会报错。因为我们此时引号没有被转义直接送入eval.触发报错。
所以,如果有着良好的FUZZ手段,黑盒也可以fuzz出问题并确认依赖错误。
最后,我们使用eval语句命令执行。当然因为不通外网没有回显所以非常狗。我最后选择把结果写静态文件。并且这里静态目录是猜了一手 public 。所以可以直接写 /proc/self/cwd/public/css/style.css 或者在爆出是app目录后直接写 /app/public/css
exp
cookie可控所有内容上面说过了。只不过测试时因为路由好用就没控cookie.
做出来后问了下出题人果然不是预期。当然这里我不太确定预期是啥,不过差的应该不多。当然聊完后出题人顺手修了下写文件的权限 :)
然后就是这种做法我自己也不是很喜欢。自己出过的nodejs题目在可以rce的情况下我都把工作目录按root权限设置。就是为了防止写文件。不过一般会配置了防止时间盲注time-based-rce,所以都会给出rce的回显
所以这里猜一手本题预期的方法是time-based-rce:
exp
flag:
darkCTF{n0d3js_l1br4r13s_go3s_brrrr!}
因为一些格式原因。我把payload用eval转化了一下,这样看起来更舒服一些.并且不用担心奇奇怪怪的引号问题 :)
不过题目我觉得很有启发性,主要是给了我一些面对nodejs黑盒处理的手段。那就是利用其容易报错的特性进行FUZZ,得到目录这样的关键信息,或者是依赖的关键信息。同时一定程度上可以推测语句,甚至不需要知道依赖漏洞的细节就能直接上手。
最后道个歉,因为把flag写到css里后自己就把css给删了。导致后面题目一直没有style.css......
后面的题在打巅峰极客摸鱼时看了看。做了个入门题跟一个User-Agent的报错注入。总之难度整体很入门就没继续看了。
没啥说的。巅峰极客体验了一把带飞的感觉。国赛就要靠自己动手丰衣足食了。加油吧。
内容回顾:
《Cheat Engine gtutorial-i386闯关记 第一关》
《Cheat Engine gtutorial-i386闯关记 第二关》
作者延续前一关变态作风,gtutorial第三关依然是不修改游戏无法过关(至少我不能!):左下角那个白色小人是玩家,画面上3个刺猬是移动障碍物,玩家触之即死。游戏提示为:将所有红色平台变绿即可开门。
整个游戏有2个难点:难点1,如果不修改游戏,玩家无法从平台A跳上平台B,更何况地面上还有刺猬捣乱,影响发挥!难点2,门开启的瞬间,这3个刺猬飞向门边,摆出副上门讨债的架势把门堵死了,玩家碰到刺猬又会过关失败,所以玩家根本无法进门。
现在进入正题,说说怎么过关,受这篇Creating a cheat table - Coordinates文章的启发,我的想法是修改玩家的坐标,让它可以到达画面的任意位置。由于这关有2个难点,所以本文分两部分介绍。
先说难点1,怎么开门: 在整个游戏中,除了玩家可以上下运动(跳跃),玩家和障碍物都可以横向运动,所以定位玩家的Y坐标要比定位X坐标简单。最开始,我在几个比较低的平台上来回跳跃来改变及定位玩家的Y坐标,但是由于刺猬的影响,Y坐标还没确定游戏就结束了。
所以我重新审视了一下游戏:作者说要让所有平台变绿才能开门,我算了下连同玩家脚下的地面一共有12块平台,作者有没有可能再程序中设置了全局变量记录变绿的平台数?让我们试一试。游戏开始时,地面已经变色,所以,在CE的value编辑框中输入1。"Scan Type"选择"Exact Value"Value Type选择"4 Byte",然后按"first scan"做初次扫描。接着再跳平台-扫描,重复几次,马上能定位存储着变色的平台的变量:
定位变量后就简单了,直接将变量值改为12,画面右边的门马上会开,但同时,游戏的后半部分情节马上开始(看看小人的表情,分明是再说WTF):
2. 刺猬堵门后,由于没有干扰,搜索玩家的坐标反而变得一马平川了。先搜索X坐标,让玩家回到画面左下角,在CE上点击"New Scan","Scan Type"设为"Unknown initial value","Value Type"设为"Float",然后点击"First Scan"。
之后,控制玩家慢慢向右移动,每次移动-暂停时,在CE中设置"Scan Type"为"Increased value"并点击"Next Scan",反复几次即可定位x坐标。最终,读者会发现x坐标在[-1,1]区间内变动:
有了存储x坐标的变量的地址,估摸着存储y坐标的变量的地址肯定在附近。由于sizeof(float)=4,所以y坐标可能距离x坐标4字节。我们需要从Cheat table中复制黏贴2次x-coor项。第一次设置"Adjust address by"的值为4("Adjust address by"意为,复制项的地址距离源项的偏移),并设置该新添加的项在CT表中的Description为"y-coor(+4)";第二次设置"Adjust address by"的值为-4,并设置该新添加的项在CT表中的Description为"y-coor(-4)":
当然刚才添加的两个CT项并不一定就是y坐标,需要通过上下跳跃来验证其正确性。在游戏中按下"p"键即可让游戏暂定,我们在玩家起跳时记录下原始数据,当玩家起跳后再次记录数据,落地后第三次记录数据。通过以上步骤可以找到x坐标的地址+sizeof(float)=y坐标的地址。有了这两个坐标变量就可以随意改变玩家的位置,比如浮空(要达到浮空的效果,记得把x坐标和y坐标两个变量Freeze)如下图:
至此,过关不成问题了。但是,总觉得少了点什么?对,没做外挂!恩,这关的外挂做什么呢?好像整个游戏中定位玩家坐标的过程最为复杂,那就做个定位外挂,用来定位玩家坐标的基址~
首先定位访问y坐标的指令,在CT表中选中y-coor项,右键"Find out what write to this address"。
回到游戏中移动玩家,这时CE的"The following opcodes write to"窗口会列出修改y坐标的指令以及存储玩家坐标的结构体的基址(Extra Info中的 "The value of the pointer needed to find this address is probabyly 01905980"):
"Extra Info"窗口中显示,访问坐标基址的指针存放在edx中,好,那我们就写个AA脚本,将edx的写到全局可被访问的内存中,脚本如下:
激活脚本后,需要在CE的CT表中创建coorbase项-点击"Add address Manually"并填写"Add address"窗口中的内容:
coorbase存放的是指针,我们最终是要通过这个指针分别访问x/y坐标,所以还要分别手头添加2个地址,并把这几项拖到Step3下形成一组。并在Step3项上右键菜单"Group config"-"Hide children when deactived":
选这个选项的原因是coorbase是在Step3脚本中创建的,当disable这个脚本时coorbase被释放,再从被释放的地址去读玩家坐标显得不合理,故如此处理。
这是最终CT表中的表项:
当再次进入这个关卡并应用这个脚本马上能获得玩家的坐标~
原文链接: https://bbs.pediy.com/thread-249551.htm
更多阅读:
X加密-反调试-DumpDex-修复指令-重打包
AOSP常见漏洞简介
XCTF 攻防世界 reverse 萌新入坑 第三题 IgniteMe writeup
Node.js原型污染攻击的分析与利用
javascript原型,原型链特点:原型链实现了继承。
JS中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。每个继承父函数的子函数的对象都包含一个内部属性proto。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
原型链的作用是用来实现继承,比如我们新建一个数组,数组的方法就是从数组的原型上继承而来的。
var arr = []
arr.map === Array.prototype.map //arr.map
是从arr.__proto__上继承下来的,arr.__proto__也就是Array.prototype。
扩展资料:
1.JS中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。
2.每个继承父函数的子函数的对象都包含一个内部属性_proto_。该属性包含一个指针,指向父函数的prototype。若父函数的原型对象的_proto_属性为再上一层函数。在此过程中就形成了原型链。
3.原型链实现了继承。原型链存在两个问题:a 包含引用类型值的原型属性会被所有实例共享。b 在创建子类型时,无法向超类型的构造函数中传递参数。
参考资料:百度百科-javascript