0x00 Pass11(str_ireplace复写绕过)
Pass11主要考察str_ireplace 函数
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
在PHP中
str_replace() 函数
定义:使用一个字符串替换字符串中的另一些字符,对大小写敏感的搜索
语法:str_replace(find,replace,string,count)
str_ireplace() 函数
定义:使用一个字符串替换字符串中的另一些字符,对大小写不敏感的搜索
语法:str_ireplace(find,replace,string,count)
可见无法通过后缀名的大小写绕过,但对于replace 函数中直接替换为空的场景通常可以用复写来绕过,例如本关中的test1.pphphp 即可在replace 后变成test1.php 。
0x01 Pass12(GET方式%00截断)
本题考查%00截断,相当于复现CVE-2006-7243,所以需将php版本设置为PHP 5.3.4以下, 并关闭magic_quotes_gpc 选项。 具体代码为:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
可见保存的路径也可以由用户所控制,GET类型的传参。因此在保存路径中修改为:
save_path=../upload/test.php%00
再上传一个正常的jpg图片即可。
这时由于受空字符%00的影响,后面所有的内容均被截断。 最终上传test.php文件。
0x02 Pass13(POST方式%00截断)
上一关中看到save_path变量是通过GET形式传参接受的,因此在url中直接使用%00截断使得后面的路径拼接不被读取。
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传失败";
}
} else {
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
而在13关中,$_POST['save_path'] 以POST形式传参时,直接使用%00无法截断,因为POST的数据不会被直接解码,需要手动使用burp中的URL-decode 解码。
解码后就变为了不可见字符,绕过POST形式的00截断。
0x03 Pass14(文件头截取判断)
可以看到题目的要求为:
1.保证上传后的图片马中仍然包含完整的一句话或webshell代码。 2.使用文件包含漏洞能运行图片马中的恶意代码。 3.图片马要.jpg,.png,.gif三种后缀都上传成功才算过关!
代码如下:
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
可见每次上传的文件都会被读取前两个字节来判断文件类型,这里判断的也就是文件头。 这里把三种文件类型的文件头加上:
PNG文件:hex格式8950,取值输出为13780
JPG文件:hex格式FFD8,取值输出为255216。
GIF文件:GIF89a,当文件第一行为GIF头时,取值输出为7173。
在burp中,GIF文件我们只需要在要发送的数据包前面加上文件头即可。 而png和jpg文件受不可见字符的影响,建议以copy /b 1.png+test.php upload.png 的命令制作图片木马。 打开后如图所示:
上传成功后使用题目中给出的链接测试文件包含是否可以执行图片木马。
0x04 Pass15(getimagesize函数绕过)
判断是否为图片的代码为:
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
使用 $info = getimagesize($filename);和 $ext = image_type_to_extension($info[2]); 取文件后缀名。 在php中getimagesize会获取7个元素的值,分别为:
索引 0 给出的是图像宽度的像素值 索引 1 给出的是图像高度的像素值 索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM 索引 3 给出的是一个宽度和高度的字符串,可以直接用于 HTML 的<image> 标签 索引 bits 给出的是图像的每种颜色的位数,二进制格式 索引 channels 给出的是图像的通道值,RGB 图像默认是 3 索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如: header(“Content-type: image/jpeg”);
上传一张示例图片返回结果为: 可以看到info[2] 的值为2,也就是JPG,与返回的相同。 在本关中,使用刚刚copy /b 1.png+test.php upload.png 生成的图片马即可绕过。
|