2018年3月20日星期二

bad rabbit磁盘加解密分析

之前分析的磁盘加解密部分的,水平有限如有错误,还请指正:P


阅读更多博文 »

2018年3月13日星期二

Hash Length Extension Attack -- WooYunCase

再贴一篇以前的doc:)

Q:前提知道md5(secret)的值,secret的长度,在str可控的情况下,能否推算md5(secret.str)的值?


Hash Length Extension Attack


例如md5长度扩展攻击,曾经了解这个攻击的时候并不懂md5的算法实现,所以先补习了一下md5的算法。下面是我从一份md5的实现中扣出的大致流程的代码(实现细节扣除了,有兴趣可以自行搜索md5的实现代码)。
# -*- coding: cp936 -*-

#对消息填充0
def message_fill(bin_message):
    #略
#附加消息的长度
def fillLength(bin_message,length):
    #略
def F(b,c,d):
    return (b&c)|((~b)&d)
def G(b,c,d):
    return (b&d)|(c&~d)
def H(b,c,d):
    return b^c^d
def I(b,c,d):
    return c^(b|~d)
#略部分细节处理代码
#第一轮
def RF_F(messageYq_512bits,CVq,q):
    a=CVq[0]
    b=CVq[1]
    c=CVq[2]
    d=CVq[3]
    messageGroup_32bits=divide_32bits(messageYq_512bits)
    for i in range(16):
        ## 略
        b=a
        a=d
        d=c
        c=tmpb
    ABCD_group=[a,b,c,d]
    return ABCD_group
#第二轮
def RF_G(messageYq_512bits,CVq,q):
    a=CVq[0]
    b=CVq[1]
    c=CVq[2]
    d=CVq[3]
    messageGroup_32bits=divide_32bits(messageYq_512bits)
    for i in range(16):
        ## 略
        b=a
        a=d
        d=c
        c=tmpb
    ABCD_group=[a,b,c,d]
    return ABCD_group
#第三轮
def RF_H(messageYq_512bits,CVq,q):
    a=CVq[0]
    b=CVq[1]
    c=CVq[2]
    d=CVq[3]
    messageGroup_32bits=divide_32bits(messageYq_512bits)
    for i in range(16):
       ## 略
        b=a
        a=d
        d=c
        c=tmpb
    ABCD_group=[a,b,c,d]
    return ABCD_group
#第四轮
def RF_I(messageYq_512bits,CVq,q):
    a=CVq[0]
    b=CVq[1]
    c=CVq[2]
    d=CVq[3]
    messageGroup_32bits=divide_32bits(messageYq_512bits)
    for i in range(16):
        ## 略
        b=a
        a=d
        d=c
        c=tmpb
    ABCD_group=[a,b,c,d]
    return ABCD_group

def Hmd5(messageYq,CVq,L):
    for i in range(L):
        ABCD_group=RF_F(messageYq[i],CVq,i)
        ABCD_group=RF_G(messageYq[i],ABCD_group,i)
        ABCD_group=RF_H(messageYq[i],ABCD_group,i)
        ABCD_group=RF_I(messageYq[i],ABCD_group,i)
        CV_q_1=add_mod32(CVq,ABCD_group)
        CVq=CV_q_1
    return CVq

def MD5encrypt():
    message=raw_input("please input your message:")
    messageLength=len(message)
    bin_messageFilled=message_fill(bin_message)
    print "msg Fill result:",bin_messageFilled
    message=fillLength(bin_messageFilled,messageLength)
    print "msg Fill length:",message
    A=0x67452301
    B=0xefcdab89
    C=0x98badcfe
    D=0x10325476
    CVq=[A,B,C,D]
    #L为512的倍数
    L=len(message)/512
    #按512分组
    messageYq=[]

    for i in xrange(L):
        messageYq.append(message[i*512:i*512+512])
    result=Hmd5(messageYq,CVq,L)  ###########压缩函数Hmd5,为MD5算法的核心
    
if __name__=="__main__":
    MD5encrypt()


  1. md5处理消息,传入的消息必须是512bit的整数倍,若不足则先进行padding,padding的方式是,第一个字节1填充,后面全0,填到留64bit为止,最后64bit用于存储消息长度。如上代码中message_fill,以及fillLength函数。

  2. md5初始化如下4个值,对padding完毕的消息每512bit分为一组进行计算,每组进行4轮处理,每轮有16次的循环:
    •     A=0x67452301
    •     B=0xefcdab89
    •     C=0x98badcfe
    •     D=0x10325476

  3. 4轮处理后得到的4个int元素的数组,处理后继续作为CVq数组,代入下一组512bit消息的处理,直到所有512bit处理结束,最后的32个字节的hash即为32位的md5值。

如上基本概述了md5算法,那么回到文章开头的问题,知道md5(secret)的值,secret的长度,在str可控的情况下,为什么能推算md5(secret.str)的值,绕过一些验证?

这又要回到md5的算法,假如secret正好是448bit的整数倍(最后64个bit用于padding消息长度),那么secret和str必然是作为两组512bit代入大循环的4轮计算,当处理到第二组512bit,即str,此时的CVq必然是md5(secret)处理得到的数组,那么我们就可以将得到的CVq数组代入512bit分组计算的循环,继续“未完成”的哈希;

假如secret不是448bit的整数倍,也不要紧,因为str是可控的,那么我们就可以自行对secret进行padding,padding到512bit,相当于帮md5算法完成了message_fill,以及fillLength函数功能,下面继续上面的分析,又可以推算出md5(secret.str)的值。



如上的分析即是哈希长度扩展攻击,也是常见于ctf比赛应用中。其实在现实中也有一些应用,可以搜到一些case,下面看一个代码审计的实例。

phpwind前台getshell(当年乌云差不多这个标题)


wooyun一哥刚爆出这个漏洞的时候,即给了闪电3刀,p牛先知先觉发到了群里,详情未出的情况下m牛瞬间就意识到secretKey的加密上出了问题,并贴出了道哥早年的一篇文章Understanding MD5 Length Extension Attack

p牛当晚也就完成了漏洞的复现分析《phpwind 利用哈希长度扩展攻击进行getshell》。
作为websecurity群里一只bindog,也尽力在第二时间参考道哥和p牛的文章完成了学习。

读了下phpwind的代码,遵循MVC开发模式,以首页上板块为例

m对应module,c对应控制器,a对应function,模版文件位于template/bss/,定位到控制器目录src/Application,run即相当于构造函数


若类下有beforeAction函数,则这个函数先于run执行。(我只跟了遍登录流程整理,如有错误还请斧正)。例如构造如下url获取数据http://127.0.0.4:88/windid/index.php?m=api&c=app&a=list

AppController继承于OpenBaseController,而这个OpenBaseController类里有个beforeAction函数

class OpenBaseController extends PwBaseController {
 
 public $app = array();
 public $appid = 0;
 
 public  function beforeAction($handlerAdapter) {
  parent::beforeAction($handlerAdapter);
  
  $charset = 'utf-8';
  $_windidkey = $this->getInput('windidkey', 'get');
  $_time = (int)$this->getInput('time', 'get');
  $_clientid = (int)$this->getInput('clientid', 'get');
  if (!$_time || !$_clientid) $this->output(WindidError::FAIL);
  $clent = $this->_getAppDs()->getApp($_clientid);
  if (!$clent) $this->output(WindidError::FAIL);
  if (WindidUtility::appKey($clent['id'], $_time, $clent['secretkey'], $this->getRequest()->getGet(null), $this->getRequest()->getPost()) != $_windidkey)
                    $this->output(WindidError::FAIL);
  
  $time = Pw::getTime();
  if ($time - $_time > 1200) $this->output(WindidError::TIMEOUT);
  $this->appid = $_clientid;
 }
可以看到这是一个验证的过程,要绕过这个验证继续看WindidUtility::appKey的实现

如上代码,可以看到$str是post或get来处理后的,假如我们能找到一处调用这个appKey函数,并将得到的hash返回前端的,那么就可以进行哈希长度扩展攻击。搜索到如下一处

前台功能如下

有返回windidkey(即appKey函数结果),调整代码,echo下, 哪些变量参与了计算

至此,还有一个问题要解决,在进行一些越权操作时,构造的url很可能因为appKey中的ksort而导致无法利用,所以在利用时注意将一些会改变排序的字段放到post变量中,在appKey中会添加到$str结尾,满足条件攻击。如padding前面这个http://127.0.0.4:88/windid/index.php?m=api&c=app&a=list

最终构造

http://127.0.0.4:88/windid/index.php?adoAvatarcavatarmapitypeflashuid2uidundefined=%80%B8%02&windidkey=4a12ee2fcb1cad2fe03fb79ae70ead8a&time=1472109820&clientid=1

Success!

水平有限,欢迎交流:)
阅读更多博文 »

2018年3月11日星期日

Conficker Virus Analysis


目录

一. 关于壳

二. Patch MS08-067 && DNS API

三. 本地创建WebServer,用作传播本体

四. 爆破内网中的IPC$

五. 感染网络磁盘&可移动磁盘

六. 端口映射

七. 对C段和公网随机IP发起攻击

八. Shellcode 分析

九. 域名生存部分

十. ROOTKIT



阅读更多博文 »

2018年3月8日星期四

MS16-098 Analysis(CVE-2016-3309)

以前分析的,搬到blog上:P

漏洞成因和heap Fengshui没做记录,网上都有相关的文章,经典paper《Exploiting MS16-098 RGNOBJ Integer Overflow on Windows 8.1 x64 bit by abusing GDI objects》,补充点文章里没记录的鸡肋:P

1. 关于heap的构造,这个是SensePost paper的中心,非常喜欢他们的配图,So列一点















2. 利用

这是一个整型溢出,造成非分页池的分配远小于所需的大小,最终BSOD。池风水之后,申请的非分页池过小,使用过程中越界修改了构造在内存中的SURFOBJ64.sizlBitmap.cy变量为0xffffffff,此被破坏的对象做ManagerBitmap,下一页的bitmap作为worker对象,Manager越界覆写worker对象,覆盖worker->pvScan0对象,获取任意地址读写能力,最终完成提权。在没有SensePost代码的前提下,要完成这个exp还需要分析一个函数AddEdgeToGET,为精确覆写SURFOBJ64.sizlBitmap.cy为-1,也就是exp中的如下代码:
       for (int l = 0; l < 0x3FE00; l++) {
points[l].x = 0x5a1f;
points[l].y = 0x5a1f;
}
points[2].y = 20;
points[0x3FE00].x = 0x4a1f;
points[0x3FE00].y = 0x6a1f;
if (!BeginPath(hMemDC)) {
fprintf(stderr, "[!] BeginPath() Failed: %x\r\n", GetLastError());
}
for (int j = 0; j < 0x156; j++) {
if (j > 0x1F && points[2].y != 0x5a1f) {
points[2].y = 0x5a1f;
}
if (!PolylineTo(hMemDC, points, 0x3FE01)) {
fprintf(stderr, "[!] PolylineTo() Failed: %x\r\n", GetLastError());
}
}

AddEdgeToGET函数对point进行处理,v10和v11循环遍历point数组。V8为rect结构,根据v10 v11的x,y及v8的top,bottom,确定是否向v9申请的空间写标志,如1,-1









在AddEdgeToGET函数中,v10 v11相同的point,基本不做处理,v9的空间也不递增。注意到代码中point[2].y = 0x14,占了157个大数组的0x20个,这个点只是为了控制v9空间的0x30不断递增。在AddEdgeToGET函数中,有3个return分支,只有最后一个return时才会递增申请池0x30,那么我们需要覆盖下一页的bitmap.size.y=-1作为manager,下下页的bitmap作为worker,下页的manager起始偏移为0x1bc0,申请池的起始为0x0fb0。那么0x20个y==0x14的point即可递增pool至manager的相应偏移?答案是确定的。

因为0x14作为pre_point.y或者now_point.y的时候会进入如下处理


















无论如何,v6_pre_y都是保存了两点中小的y值,继续向下看













申请池大小不变的返回的情况只在这两个分支满足时,而top是始终0,bottom的值为0x1f,所以一个y==20的情况即可使pool递增0x60的空间(分别作为pre和now的y),并且这个情况下的内存值的修改是达不到要求(修改sizeBitmap.y无限大),因为now<pre时修改后,后续now>pre时会改回1,所以真正修改目标地址值的point是数组中最后一个元素完成的,这个y不存在<0x1f的情况,所以会直接从前两个return返回








阅读更多博文 »

CVE-2018-4901 analysis(Adobe Acrobat Reader)

根据talos的文章,做了个poc。Escript应该是adobe reader用来解析js的引擎。根据talos的描述,是在js调用docID的时候出现了栈溢出。翻车现场在函数EScript+0x9e7c0

观察talos的截图发现栈上污染的数据并不完全来自docID参数,应该是对参数有处理过可能出现了失误。定位溢出的地方

通过调试,v3控制的buffer长度 == len(docID) / 2,我们poc里的长度为0x2fe,观察源和目的地址在栈上的位置,足以溢出缓冲区v16

继续定位长度v3的获取,栈溢出往往伴随着长度有检测却失败,而这里也像是Unicode的处理问题
可以看到v17的数据也在这里获取,而且这里的0x80符合源地址v17的大小,x2也刚好符合v16缓冲区的大小,继续分析这个函数AcroRd32_6c9b0000!CTJPEGWriter::CTJPEGWriter+0xabf94,首先把参数0x80保存在变量v4,继续获取了两个对象v11,v15

v15保存的即为docID相关信息的对象,+0x8的位置即为docID的缓冲区地址,+0x18的位置为docID的Unicode长度

根据变量的栈地址,确定v17和v18保存着docID的Unicode长度,v16保存docID的缓冲区地址,继续跟到如下位置

a4来自模块Escript传入的参数0x80,v4即函数开始保存的长度0x80,v17为docID的Unicode字符串长度,目的地址a3即为模块escript中传入的地址(ebp-0x84),可以看到这里对最大长度限制为0x80,也说明了栈溢出时源地址中只有0x80个字节来自docID,也同时说明了docID里的数据并不是覆盖在gs以及返回地址的数据,覆盖在gs和返回地址的数据应该是漏洞函数上层函数的栈桢数据。这里虽有长度的限制,但在返回时并没把检查后的长度v4返回,而是继续把v17返回了(v6为ret时的eax),正是这里的失误造成了最后的溢出

还剩一个有趣的问题,talos文章最后所述
While this function is protected with /GS which partially mitigates this vulnerability this protection can be bypassed leading to arbitrary code execution.
这个软件的所有模块都开启了safeseh/aslr/dep,在利用上最后我没有成功,不过这里还是可以过掉这些保护的,利用失败的问题最后再说一下。现在回头看一下溢出点源和目的地址在栈上的分布,那么可以知道目的地址中一定是含有gs以及ebp的数据,做个测试,先记录一下escript的加载基址,以及调用vuln func时保存的gs值
0:007> g
ModLoad: 6d080000 6d321000   C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api
0:000> p
eax=ac8c8922 ebx=00000000 ecx=08906370 edx=088fa2d0 esi=08906370 edi=085dd708
eip=6d11e7d0 esp=003ae3a8 ebp=003ae544 iopl=0         nv up ei ng nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x37ad1:
6d11e7d0 8945fc          mov     dword ptr [ebp-4],eax ss:002b:003ae540=00000000 
那么只要我们的payload长度小于0x180即可用作leak Memory,而且pdf程序是以标签的形式打开pdf文档,只要程序不退出,那么关闭文档再打开,程序在内存中的数据都是不变的。测试可以获取gs以及模块基址


最后的问题,即使Adobe Acrobat Reader所有保护都没开,又该怎么利用这个漏洞呢?

思考1. 当覆盖到漏洞函数的gs以及返回地址时的数据,是来自调用漏洞函数的上层函数的栈桢,上层函数ebp-0x3c的4个字节数据控制淹没gs和ebp的值,那么问题就变成了上层函数的栈桢数据是否可控?起码要控制4个字节,2个字节转为gs,2个字节转为返回地址。我大概调试了一下上层函数,暂时还没发现怎么去控制这4个字节。

思考2. 程序有异常处理的,虽然开了safeseh,仍可以去尝试绕过。这个我暂时还没去看,只是意识了一下 : P


Crash & Leak info PoC :

https://github.com/bigric3/CVE-2018-4901
阅读更多博文 »