当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(7) 关注此漏洞

缺陷编号: WooYun-2013-37204

漏洞标题: 代码审计系列2:Simple Down 简单下载系统 v5.4 各种SQLi、XSS

相关厂商: Simple Down

漏洞作者: LaiX

提交时间: 2013-09-16 12:06

公开时间: 2013-12-15 12:06

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 15

漏洞状态: 未联系到厂商或者厂商积极忽略

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: php+数字类型注射 安全意识不足 php源码审核 sql注射漏洞利用技巧 白盒测试 后台验证绕过

0人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2013-09-16: 积极联系厂商并且等待厂商认领中,细节不对外公开
2013-12-15: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

整个程序无任何过滤,满城尽是 SQLi 、 XSS。

详细说明:

1.拿到程序后,我们先来看看 登录页面。(login.php)



先来看看管理登录页面(admin/adminlogin.php)的核心代码

code 区域
<?php
if(!isset($_POST['adminlogin'])){
echo "<script type='text/javascript'>";
echo "document.title = '·Ç·¨·ÃÎÊ'";
echo "</script>";
echo "·Ç·¨·ÃÎÊ¡£3Ãëºó½«·µ»ØµÇ½ҳÃ棬Èç¹ûÄãµÄä¯ÀÀÆ÷²»Ö§³Ö×Ô¶¯Ìøת£¬Çëµã»÷&nbsp;<a href='index.php' style='color:green;'>ÕâÀï</a>";
echo '<script type="text/javascript">';
echo 'GoIndex()';
echo '</script>';
exit();
}
$username = strval($_POST['t_UserName']);
$password = strval($_POST['t_UserPass']);
$password = MD5($password);

if($username != "admin")
{
echo "<script type='text/javascript'>";
echo "document.title = '¹ÜÀíÔ±Õ˺ŴíÎó'";
echo "</script>";
echo "¹ÜÀíÔ±Õ˺ŴíÎó¡£3Ãëºó½«·µ»ØµÇ½ҳÃ棬Èç¹ûÄãµÄä¯ÀÀÆ÷²»Ö§³Ö×Ô¶¯Ìøת£¬Çëµã»÷&nbsp;<a href='index.php' style='color:green;'>ÕâÀï</a>";
echo '<script type="text/javascript">';
echo 'GoIndex()';
echo '</script>';
exit();
}

$check_query = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_NAME = '$username' AND USER_PASS = '$password' LIMIT 1");
if($row = mysql_fetch_array($check_query))
{
$_SESSION['username'] = $username;
$_SESSION['userid'] = $row['ID'];
echo '<script type="text/javascript">';
echo "AutoOpenURL('admin.php')";
echo '</script>';
exit();
}
else
{
echo "<script type='text/javascript'>";
echo "document.title = '¹ÜÀíÔ±µÇ½ʧ°Ü'";
echo "</script>";
echo "¹ÜÀíÔ±µÇ½ʧ°Ü£¬Çë¼ì²éÕ˺źÍÃÜÂëÊÇ·ñÕýÈ·¡£3Ãëºó½«·µ»ØµÇ½ҳÃ棬Èç¹ûÄãµÄä¯ÀÀÆ÷²»Ö§³Ö×Ô¶¯Ìøת£¬Çëµã»÷&nbsp;<a href='index.php' style='color:green;'>ÕâÀï</a>";
echo '<script type="text/javascript">';
echo 'GoIndex()';
echo '</script>';
exit();
}
mysql_free_result($result);
mysql_close($conn);

?>





code 区域
if($username != "admin")

code 区域
$password = MD5($password);

这两句发现,似乎用户名只允许使用admin 并且密码被md5加密。

现在似乎还无法绕过登录



我们再来看看用户登录页面(login.php)的核心代码

code 区域
<?php
require_once "header.php";

if(!isset($_POST['login'])){
echo "<script type='text/javascript'>";
echo "document.title = '非法访问'";
echo "</script>";
echo "非法访问。3秒后将返回登陆页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;如果您还没有账号可从&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;前往注册。";
echo '<script type="text/javascript">';
echo 'GoLogin()';
echo '</script>';
exit();
}
$username = strval($_POST['t_UserName']);
$password = strval($_POST['t_UserPass']);
$password = MD5($password);

$check_query = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_NAME = '$username' AND USER_PASS = '$password' LIMIT 1");
if( $row = mysql_fetch_array( $check_query ) ){ //登录成功
$_SESSION['username'] = $username;
$_SESSION['userid'] = $row['ID'];
echo "<script type='text/javascript'>";
echo "document.title = '登陆成功'";
echo "</script>";
echo "用户登陆成功。3秒后将跳转到网站主首页,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='index.php' style='color:green;'>这里</a>&nbsp;返回首页。如果您还没有账号可从&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;前往注册。";
echo '<script type="text/javascript">';
echo 'GoIndex()';
echo '</script>';
}
else
{
echo "<script type='text/javascript'>";
echo "document.title = '登陆失败'";
echo "</script>";
echo "用户登陆失败。3秒后将返回登陆页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;如果您还没有账号可从&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;前往注册。";
echo '<script type="text/javascript">';
echo 'GoLogin()';
echo '</script>';
}
?>





这个页面出现了一个比较经典的语句

code 区域
$check_query = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_NAME = '$username' AND USER_PASS = '$password' LIMIT 1");
if( $row = mysql_fetch_array( $check_query ) ){ //登录成功



这句SQL语句的意思是查询是否存在管理员,如果存在就提升权限。

看了一下周围的代码,没有发现任何过滤。



根据此SQL语句结构,我们构造一个"万能密码":

1.png



进入服务器后就会变成:

code 区域
"SELECT ID FROM HBDX_USERS WHERE USER_NAME = 'admin' # AND USER_PASS = '123456' LIMIT 1"



意为:查询目标表中是否存在admin用户名,而查询密码的语句已经被 # 所注释。



登录一下果然进入了:

2.png



3.png





2.再来看看注册页面。

code 区域
<?php
require_once 'header.php';

if(!isset($_POST['reg'])){
echo "<script type='text/javascript'>";
echo "document.title = '非法访问'";
echo "</script>";
echo "非法访问1。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
exit();
}
$username = strval($_POST['t_UserName']);
$disname = strval($_POST['iptNickName']);
$userqq = strval($_POST['iptCard']);
$password = strval($_POST['t_UserPass']);
$password = MD5($password);
$repass = strval($_POST['t_RePass']);
$email = strval($_POST['t_Email']);
$datetime = date("Y-m-d H:i:s");


$reg = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_NAME = '".$username."'");
$regnum = mysql_num_rows($reg);
if($regnum > 0){
echo "用户名&nbsp;<b style='color:green;'>".$username."</b>&nbsp;已经被使用<br/>";
echo "<script type='text/javascript'>";
echo "document.title = '注册失败'";
echo "</script>";
echo "用户注册失败。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
exit();
}

$reg = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_DISPLAYNAME = '".$disname."'");
$regnum = mysql_num_rows($reg);
if($regnum > 0){
echo "昵称&nbsp;<b style='color:green;'>".$disname."</b>&nbsp;已经被使用<br/>";
echo "<script type='text/javascript'>";
echo "document.title = '注册失败'";
echo "</script>";
echo "用户注册失败。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
exit();
}

$reg = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_QQ = '".$userqq."'");
$regnum = mysql_num_rows($reg);
if($regnum > 0){
echo "QQ号码&nbsp;<b style='color:green;'>".$userqq."</b>&nbsp;已经被使用<br/>";
echo "<script type='text/javascript'>";
echo "document.title = '注册失败'";
echo "</script>";
echo "用户注册失败。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
exit();
}

$reg = mysql_query("SELECT ID FROM HBDX_USERS WHERE USER_MAIL = '".$email."'");
$regnum = mysql_num_rows($reg);
if($regnum > 0){
echo "邮箱&nbsp;<b style='color:green;'>".$email."</b>&nbsp;已经被使用<br/>";
echo "<script type='text/javascript'>";
echo "document.title = '注册失败'";
echo "</script>";
echo "用户注册失败。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
exit();
}

if(mysql_query("INSERT INTO HBDX_USERS (USER_NAME,USER_DISPLAYNAME,USER_QQ,USER_PASS,USER_MAIL,REGISTERDATE,LOGINDATE) VALUES ('$username','$disname','$userqq','$password','$email','$datetime','$datetime')"))
{
echo "<script type='text/javascript'>";
echo "document.title = '用户注册成功'";
echo "</script>";
echo "用户注册成功。3秒后将跳转到登录页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='login.html' style='color:green;'>登录</a>&nbsp;或者返回&nbsp;<a href='index.php' style='color:green;'>主页</a>";
echo '<script type="text/javascript">';
echo 'GoLogin()';
echo '</script>';

}
else
{
echo "<script type='text/javascript'>";
echo "document.title = '注册失败'";
echo "</script>";
echo "用户注册失败。3秒后将返回注册页面,如果你的浏览器不支持自动跳转,请点击&nbsp;<a href='reg.html' style='color:green;'>这里</a>&nbsp;如果您已经有账号可从&nbsp;<a href='login.html' style='color:green;'>这里</a>&nbsp;前往登陆。";
echo '<script type="text/javascript">';
echo 'GoReg()';
echo '</script>';
}
?>





同样无任何过滤,根据最后一层验证的SQL语句

code 区域
if(mysql_query("INSERT INTO HBDX_USERS (USER_NAME,USER_DISPLAYNAME,USER_QQ,USER_PASS,USER_MAIL,REGISTERDATE,LOGINDATE) VALUES ('$username','$disname','$userqq','$password','$email','$datetime','$datetime')"))





我们可以构造一个form来绕过前端验证

code 区域
<form action="**.**.**.**:8080/**.**.**.**/reg.php" method="post" >
<input name="reg" value="免费注册">
<input name="t_UserName" value="admin2">
<input name="iptNickName" value="的的">
<input name="t_UserPass" value="123456">
<input name="t_RePass" value="123456">
<input name="iptCard" value="123456">
<input name="t_Email" value="123456@**.**.**.**'">
<input type="submit" value="ok">
</form>



4.png



可以看见提交之后出现报错,我们可以使用子查询构造除了HBDX_USERS表以外的 读取任意表数据的 SQL语句

因为这个站的表除了HBDX_USERS,其他的表并没有什么敏感数据故不再演示。





3.再来看看(catalogue.php)

code 区域
@$type      = strval($_GET['type']);  //得到type的值
@$page = intval($_GET['page']); //得到page的值
@$hot = strval($_GET['hot']); //得到type的值
$page = isset($_GET['page'])?intval($_GET['page']):1; //当前页数
$result = mysql_query("SELECT CODE FROM HBDX_BASEINFO WHERE TAGSECOND = 'FILESHOW'");
$filerow = mysql_fetch_array($result);
$result = mysql_query("SELECT CODE FROM HBDX_BASEINFO WHERE TAGSECOND = 'PAGENUM'");
$pagerow = mysql_fetch_array($result);
$perNumber = $filerow['CODE']; //每页显示的最大记录数
$limit_page = $pagerow['CODE'];
$startCount = ($page-1)*$perNumber;
if (empty($type))
{
echo '<script type="text/javascript">';
echo 'AutoOpenURL("index.php")';
echo '</script>';
}
else
{
echo "<script type='text/javascript'>";
echo "document.title = '$type'";
echo "</script>";
}
if (empty($hot))
{
$hot = ",ID DESC";
}
else
{
$hot = ",FILENUM DESC";
}
$result = mysql_query("SELECT * FROM HBDX_BLUE WHERE FILETYPE = '".$type."'");
$num_max = mysql_num_rows($result);
$result = mysql_query("SELECT * FROM HBDX_BLUE WHERE FILETYPE = '".$type."' ORDER BY TOP ".$hot." LIMIT $startCount,$perNumber");
echo '<table class="table"><tr>';
echo "<th>操作</th><th>名称</th><th>大小</th><th><a href='catalogue.php?type=".$type."&hot=hot' style='color:green;'>下载量</a></th><th>发布者/联盟</th><th>标签</th><th>发布时间</th>";
echo '</tr>';
while($row = mysql_fetch_array($result))
{
$fileuser = $row['FILEUSER'];
$usersinfo = mysql_query("SELECT USER_DISPLAYNAME FROM HBDX_USERS WHERE ID = '".$fileuser."'");
$usersrows = mysql_fetch_array($usersinfo);
echo "<td><a href='pages.php?id=".$row['ID']."'>详情&nbsp;&nbsp;</a>";
if( $row['FILEEXT'] == 'net' )
{
$fileid = $row['ID'];
echo "<a href='".$row['FILEURL']."' target='_blank' onClick=\"javascript:ClickNumber('$fileid');\">下载&nbsp;&nbsp;</a></td>";
}
else
{
echo "<a href='download.php?id=".$row['ID']."'>下载&nbsp;&nbsp;</a></td>";
}
echo "<td>";
if( $row['FILEEXT'] == 'torrent' )
{
echo '<a style="background:#1369A4;color:#fff;">&nbsp;&nbsp;种子&nbsp;&nbsp;</a>';
}
$pos = strpos( $row['FILEURL'], 'ed2k://|file|');
if ($pos !== false)
{
echo '<a style="background:#467500;color:#fff;">&nbsp;&nbsp;电驴&nbsp;&nbsp;</a>';
}
$baidu = strpos( $row['FILEURL'], '**.**.**.**/share/link?');
if ($baidu !== false)
{
echo '<a style="background:#BB3D00;color:#fff;">&nbsp;&nbsp;百度&nbsp;&nbsp;</a>';
}
$kuai = strpos( $row['FILEURL'], '**.**.**.**/d/');
if ($kuai !== false)
{
echo '<a style="background:#7E3D76;color:#fff;">&nbsp;&nbsp;快传&nbsp;&nbsp;</a>';
}

echo "<a href='pages.php?id=".$row['ID']."'><b>".$row['FILETITLE']."</b></a>";

if ($row['TOP'] == 0) {
echo "<img src='images/top.gif'></img>";
}
$datetoday = date("Y-m-d");
$d1 = strtotime($datetoday);
$d2 = strtotime($row['LOADDATE']);
$days = round(($d1-$d2)/3600/24);
if( $days < 10 )
{
echo "<img src='images/new.gif'></img>";
}
if( $row['FILENUM'] > 99 )
{
echo "<img src='images/hot.gif'></img>";
}
echo "</td>";

echo "<td>".$row['FILESIZE']."</td>";
echo "<td>".$row['FILENUM']."</td>";
echo "<td>".$usersrows['USER_DISPLAYNAME']."</td>";
$tags = strtok( $row['FILETAG'],',');
echo "<td>";
while( $tags ) {
echo "<a href=\"javascript:void(0)\" onClick=\"javascript:send_request('tools.php?seacher=".$tags."');\">".$tags."&nbsp;&nbsp;</a>";
$tags = strtok (",");
}
echo "</td>";
echo "<td>".$row['LOADDATE']."</td>";
echo '</tr>';
}
echo "</table>";
$pagecount = ceil($num_max/$perNumber); //总页数

if ($page == 1) {
echo "<a href='catalogue.php?type=".$type."'><strong>首页&nbsp;&nbsp;</strong></a>";
}
if ($page != 1)
{
echo "<a href='catalogue.php?type=".$type."'><strong>首页&nbsp;&nbsp;</strong></a>";
echo "<a href='catalogue.php?type=".$type."&page=".($page-1)."'><strong>上一页&nbsp;&nbsp;</strong></a>";
}
for ($i=1;$i<=$pagecount;$i++)
{
if($i <= $limit_page)
{
echo "<a href='catalogue.php?type=".$type."&page=".$i."'>$i&nbsp;&nbsp;</a>";
}
}
if ($page<$pagecount)
{
echo "<a href='catalogue.php?type=".$type."&page=".($page+1)."'><strong>下一页&nbsp;&nbsp;</strong></a>";
echo "<a href='catalogue.php?type=".$type."&page=".$pagecount."'><strong>尾页&nbsp;&nbsp;</strong></a>";
}

if ($page==$pagecount)
{
echo "<a href='catalogue.php?type=".$type."&page=".$pagecount."'><strong>尾页&nbsp;&nbsp;</strong></a>";
}

echo "第<b>".$page."</b>页 ";
echo " 共<b>".$pagecount."</b>页 ";





这个页面可控参数太多

@$type = strval($_GET['type']); //得到type的值

@$page = intval($_GET['page']); //得到page的值

@$hot = strval($_GET['hot']); //得到type的值

$page = isset($_GET['page'])?intval($_GET['page']):1; //当前页数



这个页面也同样无任何过滤。我们尝试type参数,除了出现注入以外,还出现了输出在<script>中的情况



5.png





我们闭合之后:



6.png



漏洞证明:

以上详细说明附带证明过程。

修复方案:

幸好你的后台很简单。

版权声明:转载请注明来源 LaiX@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2013-12-15 18:48 | neal ( 普通白帽子 | Rank:219 漏洞数:23 )
    0

    ....如果gpc=on 你觉得还行吗?

  2. 2013-12-15 23:46 | jk_影 ( 实习白帽子 | Rank:59 漏洞数:9 | 专注google三十年)
    0

    我也觉得gpc开了就没办法了

  3. 2013-12-24 18:57 | LaiX ( 普通白帽子 | Rank:130 漏洞数:40 | SAINTSEC)
    0

    @neal @neal 你是来关注安全的,还是来反驳我达到装逼目的的。不管有没有开,并不代表漏洞不存在。不要扭曲了乌云的思想。

  4. 2013-12-24 19:23 | 小土豆 ( 普通白帽子 | Rank:129 漏洞数:23 )
    0

    @LaiX 赞一个~ 学习到了。

  5. 2013-12-24 19:27 | neal ( 普通白帽子 | Rank:219 漏洞数:23 )
    0

    @LaiX 我扭曲了乌云的思想? 您还真会扯。 我对这个不是很懂,有问题问一下 不行吗? 难道有问题我还不能问了?

登录后才能发表评论,请先 登录