Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Memory corruption bugs in map parser #615

Closed
mmmds opened this issue Nov 26, 2021 · 2 comments
Closed

BUG: Memory corruption bugs in map parser #615

mmmds opened this issue Nov 26, 2021 · 2 comments

Comments

@mmmds
Copy link

mmmds commented Nov 26, 2021

The game client does not validate loaded maps. I found 11 places when the game crashes due to memory corruption bugs when loading a malformed map. Malformed maps may be delivered by a server the client connects to or downloaded from 3rd party services. There are at least two places where write buffer overflow occur, so these bugs may have security implications (potentially code execution instead of just crashing). For the buffer overflow crashes I did initial analysis, for the rest I just upload the test cases and brief information when it crashes.

ezQuake version: 3.2.3
OS/device including version: Ubuntu 20.04 x86_64

Steps to reproduce:

  1. move a map to qw/maps

  2. run the game

  3. open console and type map <mapname>

  4. map 477.bsp.zip
    visleafs value comes from the map file. the calculation for the buffer size (cmodel.c, line 997) is susceptible to integer overflow. In the result, less memory than necessary is allocated and memcpy in the line 1014 copies data outside the scan buffer.

File: /home/osboxes/fuzz/ezquake-source/cmodel.c
0989: static void CM_BuildPVS(lump_t *lump_vis, lump_t *lump_leafs)
[...]
0995: 	map_vis_rowlongs = (visleafs + 31) >> 5;
0996: 	map_vis_rowbytes = map_vis_rowlongs * 4;
0997: 	map_pvs = (byte *)Hunk_Alloc(map_vis_rowbytes * visleafs);
0998: 
[...]
1014:   scan = map_pvs;
1012: 	for (i = 0; i < visleafs; i++, in++, scan += map_vis_rowbytes) {
1013: 		int p = LittleLong(in->visofs);
1014: 		memcpy(scan, (p == -1) ? map_novis : DecompressVis(visdata + p), map_vis_rowbytes);

In the case of the map477.bsp:
visleafs is set to 185352, the calculation results in value 9248:

Hunk_Alloc (size=9248) at zone.c:205
205		return Hunk_AllocName(size, "unknown");

buffer ends at:

(gdb) print scan+9248
$25 = (byte *) 0x7fffee588d80 ""

We reach first iteration of the loop where 23172 bytes are copied.

(gdb) print map_vis_rowbytes 
$20 = 23172

before memcpy:

(gdb) x/128x scan+9200
0x7fffee588d50:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588d60:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588d70:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588d80:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588d90:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588da0:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588db0:	0x00000000	0x00000000	0x00000000	0x00000000
0x7fffee588dc0:	0x00000000	0x00000000	0x00000000	0x00000000

after memcpy:

(gdb) x/128x scan+9200
0x7fffee588d50:	0x01130213	0x01ae1202	0xae0102ae	0x12210201
0x7fffee588d60:	0xae1312ae	0x13212302	0x13ae0212	0x13217202
0x7fffee588d70:	0x02120102	0x12121212	0x02ae0202	0x01aeae01
0x7fffee588d80:	0xdeae12ae	0x21120101	0x1212ae21	0x1312aeae
0x7fffee588d90:	0x1313ae21	0xaeaeaeae	0x1704ab13	0xae212114
0x7fffee588da0:	0x02ae31ae	0x120213ad	0x12ae22ae	0xae1202ae
0x7fffee588db0:	0x13ae0322	0x2121ae23	0xababac13	0xaeaeae03
0x7fffee588dc0:	0x010212ae	0x12deaede	0x13ae1312	0x13130112

here we see that data was written also outside the buffer. The loop continues and at this specific case the game crashes at i=52, where p (also user controlled) modifies in pointer to point to non-accessible memory.

Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x00000000004103bf in DecompressVis (in=0x7fffb79fd860 "") at cmodel.c:967
967			if (*in) {
(gdb) print in
$31 = (byte *) 0x7fffb79fd860 ""
$ cat /proc/1114143/maps
[...]
7fffb4021000-7fffb8000000 ---p 00000000 00:00 0 
[...]
  1. map 43.bsp.zip
    The COM_Parse function takes data coming from a map and iterates over it copying it byte by byte into the fixed size (1024) buffer com_token until a specific symbol is encountered. While iterating over the data, the function does not consider com_token's size, so it may overflow and write data outside the com_token buffer affecting among others the com_argv pointer. In this case, com_argv is overwritten with map data and the game crashes later when this pointer is being accessed.
Thread 1 "ezquake-linux-x" hit Breakpoint 1, ED_ParseEdict (data=0x7fffee585d48 " \"0 -12", '(' <repeats 193 times>..., ent=0x7fffee40074c) at pr_edict.c:905
905			data = COM_Parse (data);
(gdb) print data
$17 = 0x7fffee585d48 " \"0 -12", '(' <repeats 193 times>...

(gdb) print com_token
$18 = "origin\000me\000\071\066\000\000athmatch\000entry.wad\000ume\000 {$timer} secs\000ho ^t^i^m^e :$timer\000\000 #8$qt;bind KP_MINUS stats;bind KP_LEFTARROW $qttrack #4$qt;bind KP_5 $qttrack #5$qt;bind KP_RIGHTARROW $qttrack #6$qt;bind KP_"...

(gdb) print com_argv
$19 = (char **) 0x9cf670 <largv>

Thread 1 "ezquake-linux-x" hit Breakpoint 2, COM_Parse (data=0x7fffee586322 "") at common.c:466
466					return c ? data:data-1;
(gdb) print len
$20 = 1495

(gdb) print com_token
$21 = "0 -12", '(' <repeats 1019 times>

(gdb) print com_argv
$22 = (char **) 0x2828282828282828

(gdb) c
Continuing.
Error: SV_Error: ED_ParseEntity: EOF without closing brace
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000426b94 in COM_CheckParm (parm=0x866b36 "-nohwgamma") at common.c:612
612			if (!strcmp(parm, com_argv[i]))

(gdb) print com_argv
$23 = (char **) 0x2828282828282828
  1. map 17.bsp.zip
(gdb) r
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x000000000060678e in Image_MipReduce (in=0xa3f25a0 "0\033-\t", out=0xa3f25a0 "0\033-\t", width=0x7fffffffd924, height=0x7fffffffd920, bpp=4) at image.c:393
warning: Source file is more recent than executable.
393							out[0] = (byte) ((in[0] + in[4] + in[nextrow] + in[nextrow + 4]) >> 2);
(gdb) print nextrow
$38 = 4194304
(gdb) bt
#0  0x000000000060678e in Image_MipReduce (in=0xa3f25a0 "0\033-\t", out=0xa3f25a0 "0\033-\t", width=0x7fffffffd924, height=0x7fffffffd920, bpp=4) at image.c:393
#1  0x000000000075c7a8 in GL_Upload32 (data=0x6264b00 <GL_Upload8.trans>, width=524288, height=524288, mode=2) at gl_texture.c:371
#2  0x000000000075c133 in GL_Upload8 (data=0x7fffee876cb8 "", width=1048576, height=1048576, mode=66) at gl_texture.c:476
#3  0x000000000075b38b in GL_LoadTexture (identifier=0x7fffee5f6c60 "\006\006grad", width=1048576, height=1048576, data=0x7fffee876cb8 "", mode=66, bpp=1) at gl_texture.c:569
#4  0x000000000070f564 in R_LoadBrushModelTextures (m=0x2090118 <mod_known+360>) at gl_model.c:513
#5  0x000000000071273e in Mod_LoadTextures (l=0x7ffff59870a4) at gl_model.c:605
#6  0x000000000070d0a6 in Mod_LoadBrushModel (mod=0x2090118 <mod_known+360>, buffer=0x7ffff5987090, filesize=839538) at gl_model.c:1692
#7  0x000000000070a4a2 in Mod_LoadModel (mod=0x2090118 <mod_known+360>, crash=false) at gl_model.c:253
#8  0x000000000070d6a9 in Mod_ForName (name=0xc43b44 <cl+1326212> "maps/dm666.bsp", crash=false) at gl_model.c:265
#9  0x000000000053ae88 in Model_NextDownload () at cl_parse.c:733
#10 0x000000000053b3cb in Sound_NextDownload () at cl_parse.c:782
#11 0x00000000005405f1 in CL_ParseModellist (extended=false) at cl_parse.c:1778
#12 0x000000000054d639 in CL_ParseServerMessage () at cl_parse.c:3900
#13 0x0000000000536e1c in CL_ReadPackets () at cl_main.c:1573
#14 0x0000000000534ca6 in CL_Frame (time=0.0052128790000000036) at cl_main.c:2366
#15 0x0000000000450f4e in Host_Frame (time=0.0052128790000000036) at host.c:468
#16 0x000000000082bed5 in main (argc=1, argv=0x7fffffffe108) at sys_posix.c:342
(gdb) print width
$39 = (int *) 0x7fffffffd924
(gdb) print *width
$40 = 524288
(gdb) x/x width
0x7fffffffd924:	0x00080000
(gdb) x width
0x7fffffffd924:	0x00080000
(gdb) print *width
$41 = 524288
(gdb) print *height
$42 = 524288
(gdb) print y
$43 = 0
(gdb) print bpp
$44 = 4
(gdb) 
  1. map 0.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000410599 in CM_SetParent (node=<error reading variable: Cannot access memory at address 0x7fffff7feff8>, parent=<error reading variable: Cannot access memory at address 0x7fffff7feff0>)
    at cmodel.c:644
644	{
(gdb) bt
#0  0x0000000000410599 in CM_SetParent (node=<error reading variable: Cannot access memory at address 0x7fffff7feff8>, parent=<error reading variable: Cannot access memory at address 0x7fffff7feff0>)
    at cmodel.c:644
#1  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#2  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#3  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#4  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#5  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#6  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#7  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#8  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#9  0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#10 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#11 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#12 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#13 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#14 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#15 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#16 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#17 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#18 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#19 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#20 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#21 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#22 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#23 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#24 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#25 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#26 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#27 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#28 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#29 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#30 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#31 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#32 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#33 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#34 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#35 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#36 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#37 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#38 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#39 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#40 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#41 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#42 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#43 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
#44 0x0000000000410632 in CM_SetParent (node=0x7fffee583d00, parent=0x7fffee583d00) at cmodel.c:648
  1. map 75.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x000000000040bd5c in FindTouchedLeafs_r (node=0x7fffee583f30) at cmodel.c:505
505			sides = BOX_ON_PLANE_SIDE(leafs_mins, leafs_maxs, splitplane);
(gdb) x/x splitplane 
0x80012e583054:	Cannot access memory at address 0x80012e583054
  1. map 76.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x000000000071646d in CalcSurfaceExtents (s=0x7fffee5aae30) at gl_model.c:1117
warning: Source file is more recent than executable.
1117				val = v->position[0] * tex->vecs[j][0] +
(gdb) x/x v
0x8000af196e8c:	Cannot access memory at address 0x8000af196e8c
(gdb) x/x tex
  1. map 78.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000750253 in R_BuildLightMap (surf=0x7fffee5acb40, dest=0x211a184 <lightmaps+95988> "", stride=512) at gl_rsurf.c:403
warning: Source file is more recent than executable.
403					*bl++ += lightmap[i] * scale;
(gdb) x/x lightmap
0x8000215a2568:	Cannot access memory at address 0x8000215a2568
  1. map 79.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000716e19 in SetTextureFlags (out=0x7fffee5f2008) at gl_model.c:1157
1157		if (ISSKYTEX(out->texinfo->texture->name)) {	// sky
(gdb) x/x out
0x7fffee5f2008:	0x00000000
  1. map 83.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000711fef in Mod_LoadTextures (l=0x7ffff5a10264) at gl_model.c:543
543			mt->width  = LittleLong (mt->width);
(gdb) x/x mt
0x7fffa1a5cb08:	0x00000000
  1. map 87.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x0000000000757601 in R_RecursiveWorldNode (node=0x7fffee601630, clipflags=10) at gl_rsurf.c:1448
1448		dot = PlaneDiff(modelorg, plane);
(gdb) x/x modelorg
0x20dbd34 <modelorg>:	0x44720400
(gdb) x/x plane
0x800000e21244:	Cannot access memory at address 0x800000e21244
  1. map 90.bsp.zip
Thread 1 "ezquake-linux-x" received signal SIGSEGV, Segmentation fault.
0x00000000007574e2 in R_RecursiveWorldNode (node=0x7fffee5c1dc0, clipflags=0) at gl_rsurf.c:1431
1431					(*mark)->visframe = r_framecount;
(gdb) print (*mark)->visframe
Cannot access memory at address 0x3001df001ed
@meag
Copy link
Contributor

meag commented Nov 27, 2021

Hi

This is brilliant, thankyou very much. I'll try and get patches to fix these when I'm set up at home again (combination of really bad time at work and the wall-socket hissing, so can't use normal PC at the moment). Can I ask where you got the test cases from, is there a suite somewhere or did you create them yourself?

Thanks again,
meag

@mmmds
Copy link
Author

mmmds commented Nov 29, 2021

Hi meag, the test cases are the result of fuzzing with AFL++. As an initial corpus I took original maps that are downloaded by the nquake installer script.

meag added a commit to meag/ezquake-source that referenced this issue Dec 28, 2021
meag added a commit to meag/ezquake-source that referenced this issue Dec 28, 2021
There are probably more to add...

Fixes QW-Group#615
Reported by mmmds
meag added a commit to meag/ezquake-source that referenced this issue Dec 28, 2021
@meag meag closed this as completed in ad97fad Dec 28, 2021
meag added a commit to meag/ezquake-source that referenced this issue Dec 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants