The main reason why NGX Lua WAF was chosen as its WAF was that it had a good balance between scalability and performance.
The flexibility and efficiency of lua are unmatched by many script layers WAF.
NGX Lua WAF itself is relatively simple, and there are many false positives, false positives and bypasses. I'll sort them out as follows to improve my WAF.
1. Debug function
Prepare a debug function to facilitate debugging later. Since WAF runs in the background, you can't see the output. It's better to write it to the file in the form of log.
2. WAF can be bypassed by HPP
As a clerical error of the author (I think), I submitted it to Wuyun: http://wooyun.org/bugs/wooyun-2010-0104525
When it's open, you can modify it with the methods in it.
3. Use white list to bypass
In wafconf / whiteurl, the whitelist URL is directly / 123/
Then in the function whiteurl
The ngx.var.request'uri is compared with the '/ 123 /', as long as / 123 / exists in the URI, it will not be detected as a white list, so we can bypass the defense rules through / WAF. PHP? A = / 123 / & B =.. / etc / passwd.
So, change / 123 / to ^ / 123/
In this way, only URIs starting with / 123 / can enter the white list.
4. Is regular m or s
People who wrap WAF around a lot must know the meaning of "." in regular.
Normally,. Matches all characters of "without line breaks.". So some WAFS use this kind of regularity:
union.*select
union.*select
To intercept the injection. We can use union% 0aselect to bypass the middle one.
So, now the general WAF will use s to decorate the regular. S means single, that is, single line mode.
To put it bluntly, if s is added, then "." will match the newline.
In our NGX Lua WAF, all the regularities are decorated with m, which means multiple, multi line, that is, the default. Does not match newline. ~~(Note: this understanding is wrong, see comments for details.)
In our NGX ﹣ Lua ﹣ WAF, I is not used to modify the regular, so the default is to match multiple lines, that is to say, the default is not to match the newline.
For example, intercepting get variables:
It can be seen that ngxmatch (unescape (data), rule, "imjo") is decorated with imjo. We can bypass WAF with union% 0aselect:
5. Kill by mistake! Upload the file by mistake.
Students who understand HTTP protocol must be aware that post can be divided into two types: application / x-www-form-urlencoded and multipart / form data. The former is used when post data is used by default, and the server will decode the URL after obtaining the data. The latter is usually used when uploading. The server will not decode the URL after obtaining the data, so we can upload the binary directly.
During the upload process of PHP, the form of the uploaded file will be put into $'u files variable, and other post forms will be put into $'u post variable, which has the same effect as the direct application / x-www-form-urlencoded.
This part of post variables need special handling in Lua. The author of the original NGX Lua WAF also considered it. See waf.lua for the specific interception code. But the author handled it too hastily, and directly put the whole data package, bit by bit, into the body function for detection. This creates two problems:
- Part of the data package is sent, and part of it is put into the body for detection. Then, if unio N and select are separated from each other, such as "unio" and "N select", it is normal to detect these two packets respectively. However, when it is actually sent to PHP, it is connected together, which causes the WAF to be bypassed.
- The special characters in the file are also blocked, so-called manslaughter. Sometimes we need to upload some files. There may be HTML tags or SQL statements in the files. Here, he put the content of the uploaded form into the body detection, resulting in many files can not be uploaded.
I have modified and dealt with the above problems, but I won't write too much code in the article. The idea is as follows:
First, get the complete packets and use boundary to divide them into arrays. Traverse the array, only intercept the value of the post variable, not the file content. But you need to block the "filename = XXX" part of the file form.
6. Humanized prompt information
Although 80% of my WAF intercepts are attackers, there may be normal visitors. At this time, I need to tell the visitors that what you input is unreasonably intercepted (killed by mistake) by me. You can input or notify me in another way.
I add the following code at the top of init.lua:
File403 is my own 403 page, read it. And change the say? HTML function to this:
Change {IP}, {host}, {reason} in HTML to specific information. You can send out a prompt after the user is blocked:
If SEO needs to be optimized, I will change ngx.exit (200) to 403 to avoid this page included in the search engine. But later it was found that the status code did not change.
After studying for a while, it is found that if the content is output before ngx.exit, the parameter 403 in the exit will be invalid. Before you exit, you need to use ngx.status = ngx.http ﹤ for bidden, and set the status to ngx.http ﹤ for bidden, that is, 403.
7. Use Lua NGX WAF to prevent stealing chain
In the past, the anti-theft chain was configured with nginx's own modules, but sometimes the flexibility is not high. With Lua WAF, you can flexibly defend against stealing chains.
I've probably written a rudimentary prototype, which needs more refined configuration, and you need to modify it slowly in the future.
The 8. Epilogue
Through the research on NGX Lua WAF in recent days, I also have preliminary contact with the attack and defense of WAF. I also know that sometimes when our researchers say that we can bypass WAF, it seems that they always blame the developers of WAF. So and so didn't consider that they could bypass it. In fact, it's not easy to do WAF. It's often because of business efficiency, compatibility and other reasons that the written code is bypassed.
Sometimes, security has to make way for business. Sometimes, we know it's not safe to write like this, but some users need such packets. We can't abandon these users, so we can only try our best to change their habits and write code with better compatibility.
What I hope is that through my own research, more people will know how WAF is made, what problems will be encountered, and what bypass methods are available.
Attack and defense is just the old saying: know yourself and know your enemy, and never lose a hundred battles.
I have sorted out three diaries I wrote in the past few days, from the configuration and installation of lua WAF to the final custom script, hoping to help the same learners:
http://mp.weixin.qq.com/s?__biz=MzA4MDU0NzY4Ng==&mid=207159087&idx=1&sn=eb914d63344f5cfb4ca1f05049ddb9a3#rd
http://mp.weixin.qq.com/s?__biz=MzA4MDU0NzY4Ng==&mid=207219219&idx=1&sn=e2183ae2db2ca496bddee4ff8a4ea4bd#rd
http://mp.weixin.qq.com/s?__biz=MzA4MDU0NzY4Ng==&mid=207396212&idx=1&sn=44d649db48c4f33b2e5f33e4d74bde5b#rd
Welcome to WeChat's official account of the white hat. Safety notes worth reading before going to bed.