1.cookieAPI真难用,解析解析你造过相关的源码轮子吗
2.Gitlab Cookie 反序列化漏洞研究
3.CTF篇(攻防世界)
cookieAPI真难用,你造过相关的解析解析轮子吗
前言
歌德说过:读一本好书,就是源码在和高尚的人谈话。同理,解析解析读优秀的源码cpuminer 源码开源项目的源码,就是解析解析在和优秀的大佬交流,是源码站在巨人的肩膀上学习——今天我们将通过读js-cookie的源码,来学会造一个操作cookie的解析解析轮子~
1.准备简单介绍一下cookieCookie是直接存储在浏览器中的一小串数据。它们是源码HTTP协议的一部分,由RFC规范定义。解析解析最常见的源码用处之一就是身份验证我们可以使用document.cookie属性从浏览器访问cookie。
这个库,解析解析是源码干啥的?不用这个库时?cookie的原生API,非常“丑陋”:
修改我们可以写入document.cookie。解析解析但这不是一个数据属性,它是一个访问器(getter/setter)。对其的赋值操作会被特殊处理。对document.cookie的写入操作只会更新其中提到的cookie,而不会涉及其他cookie。例如,此调用设置了一个名称为user且值为John的cookie:
document.cookie?=?"user=John";?//?只会更新名称为?user?的?cookiedocument.cookie?=?"user=John;?path=/;?expires=Tue,??Jan??::?GMT"赋值时传入字符串,并且键值对以=相连,亚马逊源码是什么如果多项还要用分号;隔开...
删除将过期时间设置为过去,自然就是删除了~
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";但是很明显,这语义化也太差了..
js-cookieAPI我们先来了解一下API
//?setCookies.set('name',?'value',?{ ?expires:?7,?path:?''?})//?get?Cookies.get('name')?//?=>?'value'Cookies.get()?//?=>?{ ?name:?'value'?}//?removeCookies.remove('name')OK我们大概可以知道是这样子
set(key,?value)get(key)remove(key)简洁方便多了,并且一眼就知道这行代码是在干什么~
2.读源码三部曲?这段可能有点太细了,如果嫌啰嗦,只想看实现可以直接跳到下面的实现部分~
一?READMEwhy一个简单、轻量级的JavaScriptAPI,用于处理cookie适用于所有浏览器?接受任何字符大量的测试?不依赖支持ES模块支持AMD/CommonJSRFC兼容的有用的Wiki?启用自定义编码/解码<字节gzip!
优点多多呀
表示后文会详细提及~BasicUsage大概就是前面写过的API介绍
二package.json依赖确实是很少依赖,并且只有开发依赖,没有生产依赖,很nice~
scripts"scripts":?{ "test":?"grunt?test","format":?"grunt?exec:format","dist":?"rm?-rf?dist/*?&&?rollup?-c","release":?"release-it"},exportsexports":?{ ".":?{ "import":?"./dist/js.cookie.mjs","require":?"./dist/js.cookie.js"},看来入口在/dist/js.cookie这点从index.js也能看出
module.exports?=?require('./dist/js.cookie')当然,目前是没有dist这个目录的。这需要打包~
.mjs另外我们刚才看到了.mjs这个后缀,这我还是第一次见,你呢
.mjs:表示当前文件用ESM的方式进行加载
.js:采用CJS的方式加载。
ESM和CJSESM是将javascript程序拆分成多个单独模块,并能按需导入的标准。和webpack,babel不同的是,esm是javascript的标准功能,在浏览器端和nodejs中都已得到实现。也就是宠物商城源码vue熟悉的import、exportCJS也就是commonJS,也就是module.exports、require。
更多介绍以及差别不再赘述~
三src进入src,首当其冲的就是api.mjs,这一眼就是关键文件啊?emm..一个init方法,其中包含set和get方法,返回一个Objectremove方法藏在其中~乍一看,代码当然还是能看得懂每行都是在做啥的呀~但是总所周知开源项目也是不断迭代出来的~也不是一蹴而就的——若川哥
okok,我们来一步步"抄"一下源码
3.实现?下面为了传参返回值更加清晰用了TS语法~
3.1最简易版本set设置一个键值对,要这样
document.cookie?=?`${ key}=${ value};?expires=${ expires};?path=${ path}`除了键值对还有后面的属性~可别把它忘记了我们用写一个接口限制一下传入的属性:
interface?Attributes?{ path:?string;?//可访问cookie的路径,默认为根目录domain?:?string;?//可访问?cookie?的域expires?:?string?|?number?|?Date?//?过期时间:UTC时间戳string?||?过期天数[`max-age`]?:number?//ookie?的过期时间距离当前时间的秒数//...}const?TWENTY_FOUR_HOURS?=?e5?//h的毫秒数//源码中是init的时候传入defaultAttributes,这里先暂做模拟const?defaultAttributes:?Attributes?=?{ path:?'/'}function?set(key:?string,?value:?string,?attributes:?Attributes):?string?|?null?{ attributes?=?{ ...defaultAttributes,?...attributes}?//if?(attributes.expires)?{ //如果有过期时间//?如果是数字形式的,就将过期天数转为?UTC?stringif?(typeof?attributes.expires?===?'number')?{ attributes.expires?=?new?Date(Date.now()?+?attributes.expires?*?TWENTY_FOUR_HOURS)attributes.expires?=?attributes.expires.toUTCString()}}//遍历属性键值对并转换为字符串形式const?attrStr?=?Object.entries(attributes).reduce((prevStr,?attrPair)?=>?{ const?[attrKey,?attrValue]?=?attrPairif?(!attrValue)?return?prevStr//将key拼接进去prevStr?+=?`;?${ attrKey}`//?attrValue?有可能为?truthy,所以要排除?true?值的情况if?(attrValue?===?true)?return?prevStr//?排除?attrValue?存在?";"?号的情况prevStr?+=?`=${ attrValue.split(';?')[0]}`return?prevStr},?'')return?document.cookie?=?`${ key}=${ value}${ attrStr}`}get//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";0我们知道document.cookie长这个样子,那么就根据对应规则操作其字符串获得键值对将其转化为Object先
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";1要注意的有意思的一个点是,可能value中就有'='这个字符,所以还要特殊处理一下~
比如他就是"颜文字==_="?(~~应该不会有人真往cookie里面放表情吧hh~~但是value中有'='还是真的有可能滴~?其实一开始我真没想过这个问题,是看了源码才知道的
Record接收两个参数——keys、values,使得对象中的key、value必须在keys、mqtt 群发java源码values里面。
removeremove就简单啦,用set把过期时间设置为过去就好了~
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";.2接受任何字符从技术上讲,cookie的名称和值可以是任何字符。为了保持有效的格式,它们应该使用内建的encodeURIComponent函数对其进行转义~再使用ecodeURIComponent函数对其进行解码。还记得README中写的接收任何字符吗~这就需要我们自己来在里面进行编码、解码的封装~
set//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";3get//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";.3封装编码和解码两个操作源码中converter.mjs封装了这两个操作为write和read,并作为defaultConverter导出到api.mjs,最后作为converter传入init——降低了代码的耦合性,为后面的自定义配置做了铺垫~前面编码解码变成了这样:
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";.4启用自定义编码/解码我们是具有内置的encodeURIComponent和decodeURIComponent,但是也并不是必须使用这两个来进行编码和解码,也可以用别的方法——也就是前面README中说的可以自定义编码/解码~除了这两个方法可自定义,其余的属性也可以自定义默认值,并且配置一次后,后续不用每次都传入配置——所以我们需要导出时有对应的两个方法
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";6封装在其中,利用对象合并时有重复属性名的情况是后面的覆盖掉前面的这一特性完成该自定义配置属性以及转换方法的功能。现在的cookie大概是这样的一个对象
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";.5防止全局污染现在的cookie直接在全局上下文下,很危险,谁都能更改,而且还不一定能找到,我们将其设置为局部的宠物养成系统源码,封装到init函数中,调用init传入相应的自定义属性以及自定义转换方法得到一个初始化的cookie对象现在大概就是源码的架构形状了~
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";.6确保一些属性不会给改变用Object.create来生成对象,并用Object.freeze把对象atributes和converter冻结。
//?删除?cookie(让它立即过期)document.cookie?=?"expires=Thu,??Jan??::?GMT";document.cookie?=?"user=John;?max-age=0";9Obecj.create的第二个参数
属性描述符
现在你就不能修改Cookie的attributes、converter属性了~
4.总结&收获?总结init及其中属性&返回而用init函数生成对象是为了解决全局污染问题,并且更新对象时也是用的init现在你再回头看源码是不是就更加清晰了~
扩展说到cookie这个在浏览器中存储数据的小东西,就不得不提一下localstorage、sessionStorage
cookie、localstorage、sessionStorage的区别Web存储对象localStorage和sessionStorage也允许我们在浏览器上保存键/值对。
那他们的区别呢
在页面刷新后(对于sessionStorage)甚至浏览器完全重启(对于localStorage)后,数据仍然保留在浏览器中。默认情况下cookie如果没有设置expires或max-age,在关闭浏览器后就会消失
与cookie不同,Web存储对象不会随每个请求被发送到服务器,存储在本地的数据可以直接获取。因此,我们可以保存更多数据,减少了客户端和服务器端的交互,节省了网络流量。大多数浏览器都允许保存至少2MB的数据(或更多),并且具有用于配置数据的设置。
还有一点和cookie不同,服务器无法通过HTTPheader操纵存储对象。一切都是在JavaScript中完成的。
以及..他们的原生API比cookie的"好看"太多~[doge]
CookiesessionStoragelocalstorage生命周期默认到浏览器关闭,可自定义浏览器关闭除非自行删除或清除缓存,否则一直存在与服务器通信/post/Gitlab Cookie 反序列化漏洞研究
在最近的hackthebox挑战中,我遇到了一台名为Laboratory的靶机,利用Gitlab的任意文件读取漏洞进行getshell的过程让我感到好奇。尽管网上的walkthrough大多跳过了细节,直接展示如何利用MSF工具,我决定深入研究这个漏洞的原理。
漏洞起源于Hackone的一份报告,由William Bowling (vakzz)提交,指出Gitlab的UploadsRewriter函数存在文件名验证漏洞,可导致任意文件读取。最初漏洞评估价值美元,但随后作者gitlab-org在gitlab.com上发现,当cookies_serializer默认设置为:hybrid时,可能导致远程命令执行漏洞。他提供了一个生成payload的Ruby代码示例,这让我初时有些困惑。
在尝试执行这段代码时,我遇到了问题,因为对Ruby不熟悉。尽管在Kali的irb中进行了尝试,却遭遇了错误。作者通过发送GET数据包成功执行了命令,这个过程在gitlab重新评估后价值提升到了美元。然而,我起初并不明白为何仅凭一个任意文件读取漏洞就能getshell,于是查找了作者的PDF文件和相关文章来理解。
PDF中提到,Gitlab的session cookie由客户端控制,当接收到cookie时,Rails会通过secret_token验证其合法性。恶意用户可以伪造包含序列化对象的cookie,只要能伪造签名,就能在服务器上执行任意代码。关键在于如何序列化,作者建议使用Ruby的erb模块构造。
在找不到相关文章的情况下,我转向了Metasploit框架的源码,通过multi/http/gitlab_file_read_rce模块进行攻击。源码分析中,我看到了exploit函数和构建反弹shell的过程,通过层层解码,最终提取出可以生成payload的Ruby脚本。
为了方便日后使用,我编写了简化版的Ruby脚本,并成功测试了生成的Cookie,验证了命令执行。接着,我尝试将Ruby脚本转化为Python,但过程中遇到了编码问题,经过多次修改和调试,最终找到了将Ruby序列化后的payload转换到Python的方法,成功实现了反弹shell。
这个过程耗时两天两夜,主要问题在于我的技术水平不够。通过这次研究,我对Gitlab Cookie的反序列化漏洞有了更深的理解。
CTF篇(攻防世界)
欢迎来到CTF攻防世界的入门指南,深入探索每个挑战的奥秘:挖掘源代码的隐藏线索: 使用F开发者工具或快捷键Ctrl+U,探索flag的秘密所在。
解锁robots协议的智慧: 探索robots.txt文件,flag可能就隐藏在其规则之中。
备忘文件的细微之处: 观察.bak文件格式,洞察隐藏在备份中的flag。
理解Cookie的力量: 通过burp suite的抓包工具,解读cookie.php,破解信息加密。
前端知识的实战应用: 破解disabled属性,展现你的代码技巧,寻找flag。
登陆验证挑战: 挑战暴力破解或直接输入,解锁神秘账户。
php逻辑的逻辑迷宫: 探索构造参数a和b,遵循条件判断的线索。
http请求的新维度: 利用get提交a=1,post提交b=2,理解不同方法的策略。
xff和referer的伪装术: 深入理解这两个技术,按照提示操作,揭示隐藏的网络世界。
在修改IP、抓包的旅程中,你会发现flag正静静等待着你的发现。 体验一句中国菜刀工具的魔力,输入url和密码,解锁flag.txt的神秘面纱。 通过ping测试,确认你的Linux目标系统,然后运用ls命令,一步步接近flag的所在地。 当密码保护的网页不再神秘,解码URL后,flag的光芒将为你照亮前行的道路。 新手web挑战告一段落,但php进阶之旅才刚刚开始。期待在下一次的探索中,与你共同见证更多技术的精彩碰撞。2024-12-23 01:14
2024-12-23 01:07
2024-12-23 00:23
2024-12-23 00:20
2024-12-23 00:17
2024-12-23 00:07
2024-12-23 00:04
2024-12-22 23:39