libX11 DoS 漏洞 – CVE-2023-43786
2024-1-19 09:59:32 Author: Ots安全(查看原文) 阅读量:12 收藏

Ots安全

CVE-2023-43786 漏洞本质上是由于递归停止条件计算不正确而导致的无限循环。

修复提交:

https://gitlab.freedesktop.org/xorg/lib/libx11/-/commit/204c3393c4c90a29ed6bef64e43849536e863a86

XPutImage是 libX11 中的一个函数,可让您将图像放置到 X Drawable(通常是 X Window)上。使用此函数,可以将像素信息从 XImage 结构传输到指定的可绘制对象(如窗口或像素图),并根据需要对其进行定位。

xpmCreatePixmapFromImage


libXpm函数xpmCreatePixmapFromImage调用此XPutImage函数:

voidxpmCreatePixmapFromImage(    Display  *display,    Drawable   d,    XImage  *ximage,    Pixmap  *pixmap_return){    GC gc;    XGCValues values;
*pixmap_return = XCreatePixmap(display, d, ximage->width, ximage->height, ximage->depth); /* set fg and bg in case we have an XYBitmap */ values.foreground = 1; values.background = 0; gc = XCreateGC(display, *pixmap_return, GCForeground | GCBackground, &values);
XPutImage(display, *pixmap_return, gc, ximage, 0, 0, 0, 0, ximage->width, ximage->height);
XFreeGC(display, gc);}

在此函数中,ximage是要显示的源图像像素数据,并将其复制到X Drawable对象(在本例中为pixmap_return)。

XPutImage


这是XPutImagelibX11 函数:

intXPutImage (register Display *dpy,    Drawable d,    GC gc,register XImage *image,int req_xoffset,int req_yoffset,int x,int y,unsigned int req_width,unsigned int req_height){                 .....      PutSubImage(dpy, d, gc, &img, 0, 0, x, y,      (unsigned int) width, (unsigned int) height,      dest_bits_per_pixel, dest_scanline_pad);      UnlockDisplay(dpy);      SyncHandle();      Xfree(img.data);return 0;  }    }
LockDisplay(dpy); FlushGC(dpy, gc);
PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) width, (unsigned int) height, dest_bits_per_pixel, dest_scanline_pad);
.........}

它调用该PutSubImage函数:

static voidPutSubImage (register Display *dpy,    Drawable d,    GC gc,register XImage *image,int req_xoffset,int req_yoffset,int x, int y,unsigned int req_width,unsigned int req_height,int dest_bits_per_pixel,int dest_scanline_pad){int left_pad, BytesPerRow, Available;
if ((req_width == 0) || (req_height == 0))return;
Available = ((65536 < dpy->max_request_size) ? (65536 << 2) : (dpy->max_request_size << 2)) - SIZEOF(xPutImageReq); if ((image->bits_per_pixel == 1) || (image->format != ZPixmap)) { [1] left_pad = (image->xoffset + req_xoffset) & (dpy->bitmap_unit - 1); BytesPerRow = (ROUNDUP((long)req_width + left_pad, dpy->bitmap_pad) >> 3) * image->depth; } else { [2] left_pad = 0; BytesPerRow = ROUNDUP((long)req_width * dest_bits_per_pixel, [3] dest_scanline_pad) >> 3; }
if ((BytesPerRow * req_height) <= Available) { [4] PutImageRequest(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, req_height, dest_bits_per_pixel, dest_scanline_pad); } else if (req_height > 1) {int SubImageHeight = Available / BytesPerRow;
if (SubImageHeight == 0) SubImageHeight = 1;
PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, (unsigned int) SubImageHeight, dest_bits_per_pixel, dest_scanline_pad);
PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset + SubImageHeight, x, y + SubImageHeight, req_width, req_height - SubImageHeight, dest_bits_per_pixel, dest_scanline_pad); } else { [5]int SubImageWidth = (((Available << 3) / dest_scanline_pad) [6] * dest_scanline_pad) - left_pad;
PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad);
PutSubImage(dpy, d, gc, image, req_xoffset + SubImageWidth, req_yoffset, x + SubImageWidth, y, req_width - SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad); }}

技术漏洞详细信息

让我们看下面的示例图像:

Available [the requested size] = (65,536 * 4) - 28 = 262,116bits_per_pixel = 32width = 90,000 pixelsheight = 1 pixel

由于图像bits_per_pixel是 32,[1] 中的条件语句将不会通过,导致我们进入 [2] 中定义的替代代码块。

然后计算BytesPerRowon [3],然后除以 8。在我们的示例中:BytesPerRow = 90000 * 32 / 8 = 360,000

在示例中,对 [4] 的检查不会通过,因为 360000 不小于请求的大小 262116,并且无法将请求宽度的单行放入单个请求中 – 这会启动对 [5] 的else检查。

这决定了单个请求中可以包含的像素数量。然后,它启动对该函数的递归调用PutSubImage以仅传递该子集,然后进行后续递归调用以管理该行的其余部分。如果需要,还可以通过额外的递归调用进一步划分剩余部分。

然而,[6] 的计算未能考虑每像素的位数,并且递归调用使请求发送 2096928 像素而不是 2096928 位——这比单个请求所能容纳的要大。

这会导致尝试分割像素线的无限循环,始终导致数字太大而无法容纳,并再次尝试该过程以使用相同的值重试。这种递归一直持续到调用堆栈耗尽为止。

错误修复更改了 [6] 的计算并考虑了bits_per_pixel. 在示例中,这将导致递归调用请求仅发送 65529 个像素,从而导致 BytesPerRow 为 262116,完全适合可用空间,从而允许递归向前推进并只需 2 次调用即可完成。

触发错误的概念验证图像示例:https://github.com/jfrog/jfrog-CVE-2023-43786-libX11_DoS/blob/main/cve-2023-43786.xpm

如何触发该错误


调用易受攻击的 libXpm 库函数的应用程序的一个示例是 CLI 实用程序sxpm,它用于在屏幕上显示 Xpm 图像。

它调用易受攻击的xpmCreatePixmapFromImageXpm 函数,然后该函数又调用易受攻击的 libX11XPutImage函数PutSubImage

感谢您抽出

.

.

来阅读本文

点它,分享点赞在看都在这里


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMjYyMzkwOA==&mid=2247503710&idx=2&sn=c18f46340b5565442daa68b491fcfd43&chksm=9a6d7db90d6d3f3a4ee12337b8aca694902cd080d882d10eb3547964219230a90852fde56dd4&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh