├── .gitignore ├── README.md ├── phpproxy.php └── test └── url_format.php /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 看情况来维护此项目,推荐使用regeorg https://github.com/sensepost/reGeorg 2 | 3 | # PHPProxy 4 | ##using 5 | phpproxy.php?url=https://github.com 6 | 7 | ##Features 8 | 使用起来好简单的,支持GET/post操作,可以下载大文件,然后这个小玩意其实是在做中间人攻击的动作,通过篡改获取到的页面来实现无缝的浏览操作. 9 | GET,Post,Download large file,Tamper Page(you can easy click hyperlink jump to target page.) 10 | 11 | ##Preview 12 | ![image](https://github.com/abcdlzy/nothing/blob/master/5FD095BA-48A0-426C-9CDD-E2B21DB4DADB.png) 13 | -------------------------------------------------------------------------------- /phpproxy.php: -------------------------------------------------------------------------------- 1 | go($url); 13 | @preg_match_all('/Content-Type:(.*?)[\r\n]+/is',$imageData->header,$contentTypeMatch); 14 | 15 | return 'data:'.$contentTypeMatch[1][0].';base64, '.base64_encode($imageData->response); 16 | } 17 | 18 | private static function endWith($source, $checkstr) { 19 | 20 | $length = strlen($checkstr); 21 | if($length == 0) 22 | { 23 | return true; 24 | } 25 | return (substr($source, -$length) === $checkstr); 26 | } 27 | 28 | private static function startWith($source,$checkstr){ 29 | return strpos($source, $checkstr) === 0; 30 | } 31 | 32 | public static function hook($url,$response) { 33 | $protocol = (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://'; 34 | 35 | //解决因为请求资源而导致的异常 36 | if(strpos($url, '.css')||strpos($url, '.js')){ 37 | // 获取当前url的根地址 38 | $hook_url_temp = $_SERVER['HTTP_REFERER']; 39 | while($hook_url_temp[strlen($hook_url_temp) - 1] != '/' && $hook_url_temp != '') { 40 | $hook_url_temp = substr($hook_url_temp,0,strlen($hook_url_temp) - 1); 41 | } 42 | 43 | if(Tamper::endWith($hook_url_temp,"://")) 44 | { 45 | $hook_target=$_SERVER['HTTP_REFERER'].'/'; 46 | } 47 | else 48 | { 49 | $hook_target = $hook_url_temp; 50 | } 51 | 52 | } 53 | else 54 | { 55 | $hook_target =$_SERVER['PHP_SELF'] . '?url='; 56 | // 获取当前url的根地址 57 | $hook_url_temp = $url; 58 | while($hook_url_temp[strlen($hook_url_temp) - 1] != '/' && $hook_url_temp != '') { 59 | $hook_url_temp = substr($hook_url_temp,0,strlen($hook_url_temp) - 1); 60 | } 61 | $hook_target = $hook_target . $hook_url_temp; 62 | 63 | //解决因为例如https://github.com后面没有加/而导致的hook路径错误问题 64 | preg_match("~^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?~i", $url, $urlmatches); 65 | 66 | @$checkrooturl=$hook_url_temp.$urlmatches[4]; 67 | if(@$checkrooturl==$url){ 68 | $hook_target = $hook_target . $urlmatches[4].'/'; 69 | } 70 | 71 | } 72 | 73 | 74 | //URLENCODE!!! 75 | 76 | preg_match_all('/=("|\')[\/]{1,2}(.*?)("|\')/is',$response,$urlMatchs); 77 | $pageUrlArray=$urlMatchs[2]; 78 | preg_match_all('/href=("|\')(.*?)("|\')/is',$response,$urlMatchs); 79 | $pageUrlArray=array_merge($pageUrlArray,$urlMatchs[2]); 80 | preg_match_all('/=("|\')http(.*?)("|\')/is',$response,$urlMatchs); 81 | $pageUrlArray=array_merge($pageUrlArray,$urlMatchs[2]); 82 | preg_match_all('/url(\("|\(\'|\()(.*?)("\)|\'\)|\))/is',$response,$urlMatchs); 83 | $pageUrlArray=array_merge($pageUrlArray,$urlMatchs[2]); 84 | 85 | if(!empty($pageUrlArray)){ 86 | //解决某些相同的短字符串匹配后,影响后面比它更长的字符串的匹配 87 | $pageUrlArrayElementsLengthArray=array(); 88 | foreach($pageUrlArray as $nowPageUrl){ 89 | array_push($pageUrlArrayElementsLengthArray,strlen($nowPageUrl)); 90 | } 91 | 92 | array_multisort($pageUrlArrayElementsLengthArray,SORT_DESC,SORT_NUMERIC,$pageUrlArray); 93 | 94 | foreach($pageUrlArray as $pageUrl){ 95 | //解决错误替换了VIEWSTATE的问题,用于验证是否是比较合法的url 96 | if(strpos($pageUrl,'.')){ 97 | $response = str_replace($pageUrl, urlencode($pageUrl) , $response); 98 | } 99 | } 100 | } 101 | 102 | 103 | if(Tamper::$enable_image_to_base64) { 104 | $response = preg_replace('/background-image:url/is', '!!!replacebgimgurl!!!', $response); 105 | $response = preg_replace('/background:url/is', '!!!replacebgurl!!!', $response); 106 | } 107 | 108 | 109 | 110 | //因为需要篡改页面,所以需要去除integrity的限定 111 | $response = preg_replace('/integrity=(\'|\")\S*(\'|\")/i', '' , $response); 112 | 113 | //计划先换成其他字符,避免被后面的正则再次替换。 114 | $response = preg_replace('/=\'http/i', '!!!replacehttp1!!!' , $response); 115 | $response = preg_replace('/=\"http/i', '!!!replacehttp2!!!', $response); 116 | $response = preg_replace('/=\'\/\//is', '!!!replace1!!!' , $response); 117 | $response = preg_replace('/=\"\/\//is', '!!!replace2!!!' , $response); 118 | $response = preg_replace('/=\'.\//is', '!!!replacedot1!!!' , $response); 119 | $response = preg_replace('/=\".\//is', '!!!replacedot2!!!' , $response); 120 | $response = preg_replace('/href=\'/is', '!!!replacehref1!!!' , $response); 121 | $response = preg_replace('/href="/is', '!!!replacehref2!!!' , $response); 122 | 123 | 124 | 125 | // 替换基本的 / 根引用 成本网址的根引用 126 | 127 | $response = preg_replace('/=\'\//is', '=\'' . $hook_target , $response); 128 | $response = preg_replace('/=\"\//is', '="' . $hook_target , $response); 129 | $response = preg_replace("/url\('\//is", 'url(\'' . $hook_target , $response); 130 | $response = preg_replace('/url\(\"\//is', 'url("' . $hook_target , $response); 131 | 132 | // 替换 http绝对引用 为 本网址的相对引用 133 | $http_abs_ref = $_SERVER['PHP_SELF'] . '?url=http'; 134 | 135 | $response = preg_replace('/!!!replacehttp1!!!/is', '=\'' .$http_abs_ref , $response); 136 | $response = preg_replace('/!!!replacehttp2!!!/is', '="' .$http_abs_ref , $response); 137 | 138 | 139 | $response = preg_replace('/!!!replace1!!!/is', '=\'' . $_SERVER['PHP_SELF'] . '?url='.$protocol , $response); 140 | $response = preg_replace('/!!!replace2!!!/is', '="' . $_SERVER['PHP_SELF'] . '?url='.$protocol , $response); 141 | $response = preg_replace('/!!!replacedot1!!!/is', '=\'' . $hook_target , $response); 142 | $response = preg_replace('/!!!replacedot2!!!/is', '="' . $hook_target , $response); 143 | $response = preg_replace('/!!!replacehref1!!!/is', 'href=\'' . $hook_target , $response); 144 | $response = preg_replace('/!!!replacehref2!!!/is', 'href="' . $hook_target , $response); 145 | 146 | 147 | if(Tamper::$enable_image_to_base64) 148 | { 149 | $response = preg_replace('/!!!replacebgurl!!!/is', 'background:url' , $response); 150 | $response = preg_replace('/!!!replacebgimgurl!!!/is', 'background-image:url' , $response); 151 | parse_str(parse_url($hook_target)['query'],$refererUrlParse); 152 | $refererUrl=$refererUrlParse['url']; 153 | preg_match_all('/background:url(\("|\(\'|\()(.*?)("\)|\'\)|\))/is',$response,$bgMatchs); 154 | preg_match_all('/background-image:url(\("|\(\'|\()(.*?)("\)|\'\)|\))/is',$response,$bgimageMatchs); 155 | $images=array_merge($bgMatchs[2],$bgimageMatchs[2]); 156 | // start with / 157 | $parse_url_refererUrl=parse_url($refererUrl); 158 | $refererRootUrl=$parse_url_refererUrl['scheme'].'://'.$parse_url_refererUrl['host'].(array_key_exists('port',$parse_url_refererUrl)?(':'.$parse_url_refererUrl['port']):''); 159 | foreach($images as $imageurl){ 160 | $readImgUrl=''; 161 | //处理以/开始的url 162 | if(Tamper::startWith($imageurl,'/')){ 163 | $readImgUrl=$refererRootUrl.$imageurl; 164 | } 165 | else{ 166 | $readImgUrl=$refererUrl.$imageurl; 167 | } 168 | $imgDataBase64=Tamper::get_image_to_base64DataUrl($readImgUrl); 169 | 170 | //替换字符串需要处理不标准的写法,如没有使用‘或者“ 171 | if(strpos($response,'background-image:url('.$imageurl.')')){ 172 | $response = str_replace($imageurl, '"'.$imgDataBase64.'"' , $response); 173 | } 174 | else{ 175 | $response = str_replace($imageurl, $imgDataBase64, $response); 176 | } 177 | } 178 | } 179 | 180 | return $response; 181 | } 182 | 183 | /** 184 | * update : 2018-04-14 12:36:34 185 | * 改了一下 fix_request_url 的逻辑, 测试参考 test/url_format.php 186 | * 使用必须要求 url 是由 http/https 开头 187 | * 188 | * @$url 输入参数 189 | * @return 如果格式化成功返回 http[s]://prefix.domainname.org/req/page.html 190 | */ 191 | public static function fix_request_url($url){ 192 | // $url=preg_replace('/http:\/\/\//i','http://',$url); 193 | // $url=preg_replace('/https:\/\/\//i','https://',$url); 194 | // return $url; 195 | if (empty($url)) return ''; 196 | $url_detail = array(); 197 | $url = urldecode($url); 198 | if (!preg_match('/^(https|http):\/{0,}(.*)$/', $url, $url_detail) || count($url_detail) != 3) return ''; 199 | $url = $url_detail[1] . '://' . preg_replace('/\/{2,}/', '/', $url_detail[2]); 200 | return $url; 201 | } 202 | 203 | } 204 | 205 | class DataTransport 206 | { 207 | public $response=""; 208 | public $header=""; 209 | public $errMsg=""; 210 | public $CURL_enable_jsonp = false; 211 | public $CURL_enable_native = true; 212 | public $CURL_valid_url_regex = '/.*/'; 213 | public $CURL_SendCookie=""; 214 | public $CURL_SendSession=""; 215 | public $CURL_mode="native"; 216 | public $CURL_CallBack=""; 217 | public $CURL_user_agent=""; 218 | public $CURL_full_headers="1"; 219 | public $CURL_full_status='1'; 220 | 221 | 222 | 223 | public function go($url, $postdata='',$mode="native") 224 | { 225 | if(function_exists("curl_init")){ 226 | return $this->Post_CURL($url, $postdata,$mode); 227 | } 228 | else 229 | { 230 | return $this->Post_FILE_GET_CONTENTS($url, $postdata); 231 | } 232 | } 233 | 234 | private function Post_CURL($url, $postdata=null,$mode="native"){ 235 | $this->errMsg=""; 236 | 237 | $this->response=""; 238 | $this->header=""; 239 | if ( !$url ) { 240 | 241 | // Passed url not specified. 242 | $contents = 'ERROR: url not specified'; 243 | $status = array( 'http_code' => 'ERROR' ); 244 | 245 | } else if ( !preg_match( $this->CURL_valid_url_regex, $url ) ) { 246 | 247 | // Passed url doesn't match $valid_url_regex. 248 | $contents = 'ERROR: invalid url'; 249 | $status = array( 'http_code' => 'ERROR' ); 250 | 251 | } else { 252 | $ch = curl_init( $url ); 253 | 254 | // 设置post数据 255 | if ( $postdata!=null ) { 256 | curl_setopt( $ch, CURLOPT_POST, true ); 257 | @curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata ); 258 | } 259 | // 设置cookie数据 260 | if ($this->CURL_SendCookie ) { 261 | $cookie = array(); 262 | foreach ( $_COOKIE as $key => $value ) { 263 | $cookie[] = $key . '=' . $value; 264 | } 265 | if ( $this->CURL_SendSession ) { 266 | $cookie[] = SID; 267 | } 268 | $cookie = implode( '; ', $cookie ); 269 | 270 | curl_setopt( $ch, CURLOPT_COOKIE, $cookie ); 271 | } 272 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 273 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 274 | curl_setopt($ch, CURLOPT_BINARYTRANSFER,true); 275 | curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:")); 276 | curl_setopt($ch, CURLOPT_HEADER, true); 277 | curl_setopt($ch, CURLOPT_HTTP_VERSION, '1.0'); // 使用 Http1.0 避免chunked 278 | curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 设置超时 279 | 280 | curl_setopt( $ch, CURLOPT_USERAGENT, $this->CURL_user_agent ? $this->CURL_user_agent : @$_SERVER['HTTP_USER_AGENT'] ); 281 | 282 | $getresponse = curl_exec($ch); 283 | list( $header, $contents ) = preg_split( '/([\r\n][\r\n])\\1/', $getresponse, 2 ); 284 | 285 | 286 | 287 | if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') { 288 | $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 289 | $this->header = substr($getresponse, 0, $headerSize); 290 | $this->response = substr($getresponse, $headerSize); 291 | } 292 | 293 | $status = curl_getinfo( $ch ); 294 | 295 | curl_close( $ch ); 296 | } 297 | 298 | // Split header text into an array. 299 | $header_text = preg_split( '/[\r\n]+/', $header ); 300 | 301 | if ( $mode == 'native') { 302 | if ( !$this->CURL_enable_native) { 303 | $contents = 'ERROR: invalid mode'; 304 | $status = array( 'http_code' => 'ERROR' ); 305 | } 306 | 307 | // Propagate headers to response. 308 | /* foreach ( $header_text as $header ) { 309 | header( $header ); 310 | } 311 | */ 312 | return $contents; 313 | 314 | } 315 | else 316 | { 317 | 318 | // $data will be serialized into JSON data. 319 | $data = array(); 320 | 321 | // Propagate all HTTP headers into the JSON data object. 322 | if ($this->CURL_full_headers) { 323 | $data['headers'] = array(); 324 | 325 | foreach ( $header_text as $header ) { 326 | preg_match( '/^(.+?):\s+(.*)$/', $header, $matches ); 327 | if ( $matches ) { 328 | $data['headers'][ $matches[1] ] = $matches[2]; 329 | } 330 | } 331 | } 332 | 333 | // Propagate all cURL request / response info to the JSON data object. 334 | if ( $this->CURL_full_status) { 335 | $data['status'] = $status; 336 | } else { 337 | $data['status'] = array(); 338 | $data['status']['http_code'] = $status['http_code']; 339 | } 340 | 341 | // Set the JSON data object contents, decoding it from JSON if possible. 342 | $decoded_json = json_decode( $contents ); 343 | $data['contents'] = $decoded_json ? $decoded_json : $contents; 344 | 345 | // Generate appropriate content-type header. 346 | $is_xhr = strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; 347 | header( 'Content-type: application/' . ( $is_xhr ? 'json' : 'x-javascript' ) ); 348 | 349 | // Get JSONP callback. 350 | $jsonp_callback = $this->CURL_enable_jsonp && isset($this->CURL_CallBack) ? $this->CURL_CallBack : null; 351 | 352 | // Generate JSON/JSONP string 353 | $json = json_encode( $data ); 354 | 355 | return $jsonp_callback ? "$jsonp_callback($json)" : $json; 356 | 357 | } 358 | } 359 | 360 | private function Post_FILE_GET_CONTENTS($url, $post = null) 361 | { 362 | $context = array(); 363 | if (is_array($post)) { 364 | ksort($post); 365 | $context['http'] = array ( 366 | 'timeout'=>10, 367 | 'method' => 'POST', 368 | 'header' => 'Content-type: application/x-www-form-urlencoded', 369 | 'content' => http_build_query($post, '', '&'), 370 | ); 371 | } 372 | $this->response=file_get_contents($url, false, stream_context_create($context)); 373 | return $this->response; 374 | } 375 | 376 | } 377 | 378 | 379 | ob_start(); 380 | 381 | 382 | 383 | @$URL=$_REQUEST['url']; 384 | 385 | 386 | if(!empty($URL)) 387 | { 388 | $dataTransport=new DataTransport(); 389 | //处理出现https:///或者http:///的问题,不是根本解决办法,有点偷懒 390 | $URL=Tamper::fix_request_url($URL); 391 | $postArray=array(); 392 | 393 | //文件处理 394 | if(!empty($_FILES)){ 395 | foreach($_FILES as $key=>$value){ 396 | move_uploaded_file($value['tmp_name'], $value['name']); 397 | $postArray[$key]='@'.realpath($value['name']); 398 | } 399 | } 400 | 401 | //处理POST数据 402 | foreach($_POST as $key=>$value){ 403 | $postArray[$key]=$value; 404 | } 405 | 406 | //处理GET 407 | foreach($_REQUEST as $key => $val){ 408 | if($key!=='url'){ 409 | $URL.='&'.$key.'='.$val; 410 | } 411 | } 412 | 413 | //获取数据 414 | $dataTransport->go($URL,$postArray); 415 | 416 | //处理数据 417 | 418 | foreach ( preg_split( '/[\r\n]+/', $dataTransport->header ) as $headertext ) { 419 | 420 | //处理因为Content-Security-Policy而导致的资源不能加载的情况 421 | $pos = strpos($headertext, 'Content-Security-Policy'); 422 | if ($pos === false) { 423 | header($headertext); 424 | } 425 | 426 | } 427 | //Hook所有url 428 | $dataTransport->response = Tamper::hook($URL, $dataTransport->response); 429 | print($dataTransport->response); 430 | 431 | //删除临时文件 432 | if(!empty($_FILES)){ 433 | foreach($_FILES as $key=>$value){ 434 | unlink($value['name']); 435 | } 436 | } 437 | 438 | } else{ 439 | echo 'url null'; 440 | } 441 | 442 | ob_end_flush(); 443 | 444 | ?> 445 | -------------------------------------------------------------------------------- /test/url_format.php: -------------------------------------------------------------------------------- 1 | ' . 29 | 'new : ' . formatURL($val) . '
'; 30 | 31 | $encode = urlencode($val); 32 | echo 33 | 'origin : ' . $encode . '
' . 34 | 'new : ' . formatURL($encode) . '
'; 35 | 36 | echo '





'; 37 | } 38 | } 39 | ?> --------------------------------------------------------------------------------