12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
三国霸业 战车-珍宝-觉醒-攻城掠地SF-全新玩法 免费玩新区 攻城掠地私服 进入游戏
手游私服盒子 各类免费游戏 0.1折送海量资源 各类手游私服 进入游戏
皇家MU2 《奇迹 2:传奇》韩国网禅公司《奇迹》正统续作。 3D锁视角Mmrpg 暗黑3+传奇+流放之路+奇迹 进入游戏
查看: 500|回复: 0

CVE-2014-9707分析以及exp构造

[复制链接]

303

主题

303

帖子

616

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
616
发表于 2019-8-20 10:53:28 | 显示全部楼层 |阅读模式
0x00 介绍
Embedthis Software GoAhead是美国Embedthis Software公司的一款嵌入式Web服务器。
Embedthis Software GoAhead 3.0.0版本至3.4.1版本中存在安全漏洞,该漏洞源于程序没有正确处理以‘.’字符开始的路径部分。远程攻击者可借助特制的URI利用该漏洞实施目录遍历攻击,造成拒绝服务(基于堆的缓冲区溢出和崩溃),也可能执行任意代码。[1]


0x01 环境
Ubuntu 15.10(I686,关闭ASLR、NX)
Goahead 3.4.1
Glibc 2.19


0x02 漏洞产生分析
瞧瞧代码
[C] 纯文本查看 复制代码for (mark = sp = dupPath; *sp; sp++) {    if (*sp == '/') {        *sp = '\0';        while (sp[1] == '/') {            sp++;        }        segments[nseg++] = mark;        len += (int) (sp - mark);        mark = sp + 1;    }}segments[nseg++] = mark;len += (int) (sp - mark);
在函数websNormalizeUriPath中,第一个for代码块,会将URI以’/‘分割,放入数组,并且统计URI字符串长度(不包括’/‘)。
来,让我们举个栗子看下。

websNormalizeUriPath收到了一个字符串参数,内容是”/hello/./world/.x”。第一个for君勤勤恳恳地工作,将字符串以’/‘分割并且统计好长度。此时各个变量的内容是这样的:
[C] 纯文本查看 复制代码segments[0]: '\0'segments[1]: 'hello'segments[2]: '.'segments[3]: 'world'segments[4]: '.x'segments[5] : '\0'len: 13nseg : 5
现在来到了最重要的时刻,第二个for君要上场工作了。先看下它长啥样。
[C] 纯文本查看 复制代码for (j = i = 0; i < nseg; i++, j++) {    sp = segments;    if (sp[0] == '.') {        if (sp[1] == '\0')  {            if ((i+1) == nseg) {                segments[j] = &quot;&quot;;            } else {                j--;            }        } else if (sp[1] == '.' && sp[2] == '\0')  {            if (i == 1 && *segments[0] == '\0') {                j = 0;            } else if ((i+1) == nseg) {                if (--j >= 0) {                    segments[j] = &quot;&quot;;                }            } else {                j = max(j - 2, -1);            }        }    } else {        segments[j] = segments;    }}
segments同时肩负输入和输出的重任,i控制输入流的偏移,j控制输出流的偏移。
此时有两种情况处理,当sp为 ‘.’ 时,做一些操作。当sp不为 ‘.’ 时,直接将输入复制到输出。
仔细瞧瞧当sp为’.’时的处理,它做了以下的动作:

  • 当下一个字符为0时,如果输出流到了末尾时((i+1) == nseg),直接复制空字符串到输出流。否则输出流不变(j–,在for的循环表达式中j++,以保持不变)
  • 当下一个字符为 ‘.’ 并且sp[2]为0时,也就是sp为 “..”时。做*操作。(这里不讲了,不是重点。)重点来了,如果sp不是上面两种情况,将会啥都不做,比如sp为”.x”的话,那么它啥也不做,并且在for的循环表达式中将i跟j自增。
继续举个栗子瞧瞧:
还是以上面的字符串为例“/hello/./world/.x”

  • ‘hello’直接从输入复制到输出
  • ‘.’,j - 1。以保持不变
  • ‘world’,将输入复制到输出。注意,在第2步中因为j不变,所以j现在是2,也就是’.’的位置。
  • ‘.x’,啥也不做,i++,j++。到这里已经结束了,nseg为5,现在i也是5了,j为4
看看调整后segments的内容:
[C] 纯文本查看 复制代码segments[0]: '\0'                // 长度 0segments[1]: 'hello'               // 长度 5segments[2]: 'world'            // 长度 5segments[3]: 'world'            // 长度 5segments[4]: '.x'                // 长度 2segments[5] : '\0'
继续往下走
[C] 纯文本查看 复制代码nseg = j;assert(nseg >= 0);if ((path = walloc(len + nseg + 1)) != 0) {    for (i = 0, dp = path; i < nseg; ) {        strcpy(dp, segments);        len = (int) slen(segments);        dp += len;        if (++i < nseg || (nseg == 1 && *segments[0] == '\0' && firstc == '/')) {            *dp++ = '/';        }    }    *dp = '\0';}
len使用的还是分割时计算的(13)。nseg被改成了j(4)。
看看上面调整后segments内字符串的长度:0 + 5 + 5 + 5 = 15。(nseg为4)
path new时的长度是13 + 4 + 1 (len + nseg + 1),而复制到path的字符串长度将是15 + 3 + 1。
很明显,在这发生了溢出。只要稍微构造一下就能触发unlink了。
漏洞分析完毕。

0x03 目录遍历
来,我们准备了这么一个字符串”/../../../../../.x/.x/.x/.x/.x/.x/etc/passwd”,在第二个for君的处理中,遇到”..”并且没到末尾的话,会将j-1,或者置0。
在处理了一连串的”..”之后,遇到了”.x”,我们知道它只会将i、j加1。看看处理完之后的segments吧
[C] 纯文本查看 复制代码(gdb) p segments[0]$9 = 0x8055a30 &quot;&quot;(gdb) p segments[1]$10 = 0x8055a31 &quot;..&quot;(gdb) p segments[2]$11 = 0x8055a34 &quot;..&quot;(gdb) p segments[3]$12 = 0x8055a37 &quot;..&quot;(gdb) p segments[4]$13 = 0x8055a3a &quot;..&quot;(gdb) p segments[5]$14 = 0x8055a3d &quot;..&quot;(gdb) p segments[6]$15 = 0x8055a52 &quot;etc&quot;(gdb) p segments[7]$16 = 0x8055a56 &quot;passwd&quot;(gdb) p segments[8]$17 = 0x8055a46 &quot;.x&quot;(gdb) p nseg $23 = 8
详情请看参考[2]的Directory traversal

0x04 远程命令执行
当执行到wfree(dupPath);的时候,内存布局大概如下:

path是能通过url控制的区域,只要溢出并且覆盖top的size(重点是覆盖点p位,置为0),这样的话,当free(segments)时,就会判断path是否为空闲,由于前面被我们将top的p位置为0,所以此时会unlink(path)。通过在path准备点蛋糕,就可以让shellcode执行了。
蛋糕打造过程:
因为glibc 2.19在unlink判断了fd和bk,所以想要直接通过fd和bk来覆盖函数地址是不可能了。
[C] 纯文本查看 复制代码#define unlink(P, BK, FD) {                                            \    FD = P->fd;                                      \    BK = P->bk;                                      \    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))              \          malloc_printerr (check_action, &quot;corrupted double-linked list&quot;, P);      \    else {                                      \        FD->bk = BK;                                  \        BK->fd = FD;                                  \        if (!in_smallbin_range (P->size)                      \            && __builtin_expect (P->fd_nextsize != NULL, 0)) {              \            assert (P->fd_nextsize->bk_nextsize == P);                  \            assert (P->bk_nextsize->fd_nextsize == P);                  \            if (FD->fd_nextsize == NULL) {                      \                if (P->fd_nextsize == P)                      \                      FD->fd_nextsize = FD->bk_nextsize = FD;              \                else {                                  \                    FD->fd_nextsize = P->fd_nextsize;                  \                    FD->bk_nextsize = P->bk_nextsize;                  \                    P->fd_nextsize->bk_nextsize = FD;                  \                    P->bk_nextsize->fd_nextsize = FD;                  \                  }                                  \              } else {                                  \                P->fd_nextsize->bk_nextsize = P->bk_nextsize;              \                P->bk_nextsize->fd_nextsize = P->fd_nextsize;              \          }                                      \      }                                      \  }                                          \}
glibc 2.19的unlink如上。

从代码可以看出,Relase模式下,是对fd_nextsize和bk_nextsize没有进行判断的,但是fd_nextsize和bk_nextsize是在large blocks才有的,所以需要构造一个大于512字节(32位系统)的块。
我构造的path:

当segments被释放时,path会被认为是已经释放了的块,所以会触发consolIDAte forward。
fd和bk都指向path的地址,以通过”corrupted double-linked list”检查。

exp执行结果:


0x05 参考
[1]SCAP中文社区
[2]Advisory: CVE-2014-9707
[3]Understanding glibc malloc
[4]EXP


</i></i></i></i>
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-11-24 13:30 , Processed in 0.078125 second(s), 30 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表