Recently, a friend's company encountered something that made them "unable to sleep and eat": their SMS verification code interface was targeted, and the money charged was not long after that. If you don’t recharge money, your business will be directly affected; but if you recharge money, it is like pouring water into a bottomless pit. They contacted the SMS service provider, and the other party reported that it might have been "maliciously stolen". Since they don’t have their own IT team, the app is outsourced and is now in a state of unmaintained. The boss hopes to find a way to help stop the bleeding without changing the code.
Deficiency: It's not a bug, but an interface running naked
This app has been running stably for about two years, and the possibility of program bugs is relatively small. We suspect that the SMS platform information is leaked, or the interface is exploited by malicious programs. I went to the server and found that their deployment was very simple: the front-end uses Nginx to directly proxy the back-end Java service. Open the Nginx log and found that someone requested to obtain the URL of the SMS verification code at a frequency of 3-6 times per second. Moreover, the interface call has not performed secondary verification, which means that the permission to "issuing verification code" is completely opened. Why! Opportunities are left to those with ulterior motives.
Repair the * for the dead
In this case, it is really difficult to change the code. From the Nginx log, we can see that the attacker used a large number of proxy IPs, and the IP and parameters of each request are changing, so there is no way to solve the problem through the IP blacklist. We can only hope that the app carries an identity that the attacker does not have when requesting the interface. Since Nginx cannot print the complete request header directly, consider using OpenResty to output all request header content to the log through Lua footsteps (this interface is a Get request). I can only work overtime when it is late at night, but fortunately I have learned a little about OpenResty before.
Analysis request header
I first tried a simple script to analyze the request header. If I can't pass this level, I can basically give up this idea.
location /api/reg/getCode {
content_by_lua_block {
for k, v in pairs(.get_headers()) do
(k, ": ", v)
end
}
}
After some analysis, it was found that the App brought a version number information on each request (although it is still at 1.0.0), which is very good. It can be used to distinguish whether it is a malicious request.
Verification based on request header
Next, use Nginx's map instruction to determine whether the request header contains the specified version number field. If you hit, you will let go, and if you miss it, you will stop it.
The code is as follows:
http {
map $http_app_version $allow {
default 0;
"~*xxx_1.0.0" 1;
}
server {
listen 80;
server_name ;
location /api/reg/getCode {
if ($allow = 0) {
return 403;
}
proxy_pass http://java_service;
}
}
}
After deploying it, I looked at the Java logs and the request for interface stolen is directly killed, with a significant effect.
Use false to make the real
Although the interception is successful, returning to 403 will expose our defense strategy, easily attract the opponent's attention, and break through this weak barrier in minutes. So an upgrade was made: after nginx intercepts the request, it directly returns the response result of the request success, and from then on both sides can maintain the illusion of "success".
Changes are as follows:
location /api/reg/getCode {
if ($allow = 0) {
add_header Content-Type application/json;
return 200 '{"code":"200","message":"Verification code sent successfully","success":true}';
}
proxy_pass http://java_service;
}
After testing, it is absolutely impossible to see if there is really a request for Java service without looking at the logs.
Summarize
I have never understood what this kind of attack is about. It does not bring direct benefits, but also costs money to make agents and scripts. Isn’t this a typical way to harm others and not benefit oneself? In the following days, everything returned to its former tranquility. Although the matter has come to an end, temporary bleeding is not a long-term solution. To solve such problems, it is necessary to strengthen program security verification and increase the difficulty of attacks.
at last
About OpenResty, please read mineOpenRestyStudy notes, welcome to like and support.