Background#
On the night of the 10th at 11 o'clock, a considerable number of people in the group discovered that the background needed to be reinitialized, and the Mongo database was cleared, leaving only a ransom letter. There were previous cases, and other members, including myself, quickly checked and found that I was also affected. Innei even specifically issued a security alert for this.
Ransom letter original text: (From wibus)
We delete all databases, but download a copy to our server. The only way of recovery is you must send 0.01 BTC to bc1qmaacz9fdvnkujqlf8m547mzzh0l5t0ajn699th. You have until 48 hours to pay or data will be inaccessible. Once paid please email incomings99112@onionmail.com with code: xxxxxx and we will recover your database. please read https://paste.sh/UY6_vtGL#THGqRdL9oQqUc-28RPDOWSbB for more information.
Of course, as an excellent CMS system, mx-space comes with automatic backup, which backs up the data of the day every day at 1 am. So I directly went to the file management and downloaded the backup files of the day and the surrounding days. On the one hand, it is convenient for recovery, and on the other hand, I shared the backup files in the group to see what people have to say.
Analysis#
There is no doubt that being hacked means there are vulnerabilities, as the saying goes, flies don't bite seamless eggs. Based on the descriptions of several users who were hacked in the group (including myself), there are common points for those who had their databases deleted: no firewall for the service provider/VPS, the Mongo container has mapped ports open and directly mapped to 0.0.0.0 (i.e., accessible from the public network), and the database has no authentication mechanism, so it was hacked.
Solutions and Follow-up Measures#
Disable unnecessary port mapping#
In the initial security alert, Innei mentioned one point, which is to disable unnecessary port mapping.
In the previous version of the Docker Compose file, both mongo and Redis had a port mapping, and since no IP was specified, Docker's default behavior would directly map it to 0.0.0.0, exposing it to the public network. However, considering that mongo and Redis are in the same network as the main container and there is no problem with the connection, the port mapping can be completely removed. The following is a partial content after removing the port mapping.
mongo:
container_name: mongo
image: mongo
volumes:
- ./data/db:/data/db
networks:
- app-network
restart: always
redis:
image: redis
container_name: redis
networks:
- app-network
restart: always
networks:
app-network:
driver: bridge
Set up a firewall#
Secondly, considering that this is a problem caused by port exposure, a UFW firewall was installed on the server to block ports. Of course, after installing it, the SSH port and 1Panel port of the server need to be allowed before starting the firewall, otherwise, it would be embarrassing if you can't connect to the server/access the panel (.
sudo apt update
sudo apt install ufw
sudo ufw allow 22/tcp # If SSH is running on a non-standard port, replace the above command with the corresponding SSH port.
sudo ufw allow 8090/tcp # Allow the 1Panel system port, replace 8090 with the panel's port
sudo ufw enable # UFW, start!
1Panel also has a corresponding firewall function, so after starting UFW, you can directly manage UFW in the panel, which is very convenient for slacking off.
Reduce container privileges#
Thanks to timo's suggestions and guidance. The operation is very simple, but my understanding of Linux is not deep enough, so I searched for 114514 commands on Baidu when I was tinkering (.
First, directly modify the Compose configuration file and add a line user: node
, as follows (excerpt from the file):
version: '3.8'
services:
app:
container_name: mx-server
image: innei/mx-server:latest
command: bash ./docker-run.sh
environment:
- TZ=Asia/Shanghai
- NODE_ENV=production
- ALLOWED_ORIGINS
- JWT_SECRET
- ENCRYPT_KEY
- ENCRYPT_ENABLE
volumes:
- ./data/mx-space:/root/.mx-space
ports:
- '2333:2333'
depends_on:
- mongo
- redis
links:
- mongo
- redis
networks:
- app-network
restart: always
user: node
healthcheck:
test:
[
'CMD',
'curl',
'-f',
'http://127.0.0.1:2333/api/v2/ping'
]
interval: 1m30s
timeout: 30s
retries: 5
start_period: 30s
After adding this constraint, the container will run commands as the node user (UID 1000), and the process ownership on the host machine will also use UID 1000 (there may not be a user with UID 1000 in the system), instead of the default root, which reduces the security risk.
However, since the container runs with UID 1000, the data inside the container is placed under /home/node/
, and the ownership of the data folder is still in the hands of root. Therefore, it is necessary to modify the container configuration file to change the location of the data inside the container and the ownership of the corresponding mapped folder on the host machine. These are basic operations in Linux, and if you don't want to use the command line, you can directly make the changes in the panel's file management, so I won't elaborate on them further.