├── .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 | 
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 | ?>
--------------------------------------------------------------------------------